Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
nv50.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24 
25 #include <subdev/mxm.h>
26 #include <subdev/bios.h>
27 #include <subdev/bios/conn.h>
28 #include <subdev/bios/dcb.h>
29 #include <subdev/bios/mxm.h>
30 
31 #include "mxms.h"
32 
33 struct nv50_mxm_priv {
34  struct nouveau_mxm base;
35 };
36 
37 struct context {
39  struct mxms_odev desc;
40 };
41 
42 static bool
43 mxm_match_tmds_partner(struct nouveau_mxm *mxm, u8 *data, void *info)
44 {
45  struct context *ctx = info;
46  struct mxms_odev desc;
47 
48  mxms_output_device(mxm, data, &desc);
49  if (desc.outp_type == 2 &&
50  desc.dig_conn == ctx->desc.dig_conn)
51  return false;
52  return true;
53 }
54 
55 static bool
56 mxm_match_dcb(struct nouveau_mxm *mxm, u8 *data, void *info)
57 {
58  struct nouveau_bios *bios = nouveau_bios(mxm);
59  struct context *ctx = info;
60  u64 desc = *(u64 *)data;
61 
62  mxms_output_device(mxm, data, &ctx->desc);
63 
64  /* match dcb encoder type to mxm-ods device type */
65  if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type)
66  return true;
67 
68  /* digital output, have some extra stuff to match here, there's a
69  * table in the vbios that provides a mapping from the mxm digital
70  * connection enum values to SOR/link
71  */
72  if ((desc & 0x00000000000000f0) >= 0x20) {
73  /* check against sor index */
74  u8 link = mxm_sor_map(bios, ctx->desc.dig_conn);
75  if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24)
76  return true;
77 
78  /* check dcb entry has a compatible link field */
79  link = (link & 0x30) >> 4;
80  if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link)
81  return true;
82  }
83 
84  /* mark this descriptor accounted for by setting invalid device type,
85  * except of course some manufactures don't follow specs properly and
86  * we need to avoid killing off the TMDS function on DP connectors
87  * if MXM-SIS is missing an entry for it.
88  */
89  data[0] &= ~0xf0;
90  if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 &&
91  mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) {
92  data[0] |= 0x20; /* modify descriptor to match TMDS now */
93  } else {
94  data[0] |= 0xf0;
95  }
96 
97  return false;
98 }
99 
100 static int
101 mxm_dcb_sanitise_entry(struct nouveau_bios *bios, void *data, int idx, u16 pdcb)
102 {
103  struct nouveau_mxm *mxm = nouveau_mxm(bios);
104  struct context ctx = { .outp = (u32 *)(bios->data + pdcb) };
105  u8 type, i2cidx, link, ver, len;
106  u8 *conn;
107 
108  /* look for an output device structure that matches this dcb entry.
109  * if one isn't found, disable it.
110  */
111  if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) {
112  nv_debug(mxm, "disable %d: 0x%08x 0x%08x\n",
113  idx, ctx.outp[0], ctx.outp[1]);
114  ctx.outp[0] |= 0x0000000f;
115  return 0;
116  }
117 
118  /* modify the output's ddc/aux port, there's a pointer to a table
119  * with the mapping from mxm ddc/aux port to dcb i2c_index in the
120  * vbios mxm table
121  */
122  i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port);
123  if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP)
124  i2cidx = (i2cidx & 0x0f) << 4;
125  else
126  i2cidx = (i2cidx & 0xf0);
127 
128  if (i2cidx != 0xf0) {
129  ctx.outp[0] &= ~0x000000f0;
130  ctx.outp[0] |= i2cidx;
131  }
132 
133  /* override dcb sorconf.link, based on what mxm data says */
134  switch (ctx.desc.outp_type) {
135  case 0x00: /* Analog CRT */
136  case 0x01: /* Analog TV/HDTV */
137  break;
138  default:
139  link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30;
140  ctx.outp[1] &= ~0x00000030;
141  ctx.outp[1] |= link;
142  break;
143  }
144 
145  /* we may need to fixup various other vbios tables based on what
146  * the descriptor says the connector type should be.
147  *
148  * in a lot of cases, the vbios tables will claim DVI-I is possible,
149  * and the mxm data says the connector is really HDMI. another
150  * common example is DP->eDP.
151  */
152  conn = bios->data;
153  conn += dcb_conn(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
154  type = conn[0];
155  switch (ctx.desc.conn_type) {
156  case 0x01: /* LVDS */
157  ctx.outp[1] |= 0x00000004; /* use_power_scripts */
158  /* XXX: modify default link width in LVDS table */
159  break;
160  case 0x02: /* HDMI */
161  type = DCB_CONNECTOR_HDMI_1;
162  break;
163  case 0x03: /* DVI-D */
164  type = DCB_CONNECTOR_DVI_D;
165  break;
166  case 0x0e: /* eDP, falls through to DPint */
167  ctx.outp[1] |= 0x00010000;
168  case 0x07: /* DP internal, wtf is this?? HP8670w */
169  ctx.outp[1] |= 0x00000004; /* use_power_scripts? */
170  type = DCB_CONNECTOR_eDP;
171  break;
172  default:
173  break;
174  }
175 
176  if (mxms_version(mxm) >= 0x0300)
177  conn[0] = type;
178 
179  return 0;
180 }
181 
182 static bool
183 mxm_show_unmatched(struct nouveau_mxm *mxm, u8 *data, void *info)
184 {
185  u64 desc = *(u64 *)data;
186  if ((desc & 0xf0) != 0xf0)
187  nv_info(mxm, "unmatched output device 0x%016llx\n", desc);
188  return true;
189 }
190 
191 static void
192 mxm_dcb_sanitise(struct nouveau_mxm *mxm)
193 {
194  struct nouveau_bios *bios = nouveau_bios(mxm);
195  u8 ver, hdr, cnt, len;
196  u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len);
197  if (dcb == 0x0000 || ver != 0x40) {
198  nv_debug(mxm, "unsupported DCB version\n");
199  return;
200  }
201 
202  dcb_outp_foreach(bios, NULL, mxm_dcb_sanitise_entry);
203  mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL);
204 }
205 
206 static int
207 nv50_mxm_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
208  struct nouveau_oclass *oclass, void *data, u32 size,
209  struct nouveau_object **pobject)
210 {
211  struct nv50_mxm_priv *priv;
212  int ret;
213 
214  ret = nouveau_mxm_create(parent, engine, oclass, &priv);
215  *pobject = nv_object(priv);
216  if (ret)
217  return ret;
218 
219  if (priv->base.action & MXM_SANITISE_DCB)
220  mxm_dcb_sanitise(&priv->base);
221  return 0;
222 }
223 
224 struct nouveau_oclass
226  .handle = NV_SUBDEV(MXM, 0x50),
227  .ofuncs = &(struct nouveau_ofuncs) {
228  .ctor = nv50_mxm_ctor,
229  .dtor = _nouveau_mxm_dtor,
230  .init = _nouveau_mxm_init,
231  .fini = _nouveau_mxm_fini,
232  },
233 };