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 #include "mxms.h" 25 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 struct nv50_mxm_priv { 32 struct nvkm_mxm base; 33 }; 34 35 struct context { 36 u32 *outp; 37 struct mxms_odev desc; 38 }; 39 40 static bool 41 mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info) 42 { 43 struct context *ctx = info; 44 struct mxms_odev desc; 45 46 mxms_output_device(mxm, data, &desc); 47 if (desc.outp_type == 2 && 48 desc.dig_conn == ctx->desc.dig_conn) 49 return false; 50 return true; 51 } 52 53 static bool 54 mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info) 55 { 56 struct nvkm_bios *bios = nvkm_bios(mxm); 57 struct context *ctx = info; 58 u64 desc = *(u64 *)data; 59 60 mxms_output_device(mxm, data, &ctx->desc); 61 62 /* match dcb encoder type to mxm-ods device type */ 63 if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type) 64 return true; 65 66 /* digital output, have some extra stuff to match here, there's a 67 * table in the vbios that provides a mapping from the mxm digital 68 * connection enum values to SOR/link 69 */ 70 if ((desc & 0x00000000000000f0) >= 0x20) { 71 /* check against sor index */ 72 u8 link = mxm_sor_map(bios, ctx->desc.dig_conn); 73 if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24) 74 return true; 75 76 /* check dcb entry has a compatible link field */ 77 link = (link & 0x30) >> 4; 78 if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link) 79 return true; 80 } 81 82 /* mark this descriptor accounted for by setting invalid device type, 83 * except of course some manufactures don't follow specs properly and 84 * we need to avoid killing off the TMDS function on DP connectors 85 * if MXM-SIS is missing an entry for it. 86 */ 87 data[0] &= ~0xf0; 88 if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 && 89 mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) { 90 data[0] |= 0x20; /* modify descriptor to match TMDS now */ 91 } else { 92 data[0] |= 0xf0; 93 } 94 95 return false; 96 } 97 98 static int 99 mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb) 100 { 101 struct nvkm_mxm *mxm = data; 102 struct context ctx = { .outp = (u32 *)(bios->data + pdcb) }; 103 u8 type, i2cidx, link, ver, len; 104 u8 *conn; 105 106 /* look for an output device structure that matches this dcb entry. 107 * if one isn't found, disable it. 108 */ 109 if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) { 110 nv_debug(mxm, "disable %d: 0x%08x 0x%08x\n", 111 idx, ctx.outp[0], ctx.outp[1]); 112 ctx.outp[0] |= 0x0000000f; 113 return 0; 114 } 115 116 /* modify the output's ddc/aux port, there's a pointer to a table 117 * with the mapping from mxm ddc/aux port to dcb i2c_index in the 118 * vbios mxm table 119 */ 120 i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port); 121 if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP) 122 i2cidx = (i2cidx & 0x0f) << 4; 123 else 124 i2cidx = (i2cidx & 0xf0); 125 126 if (i2cidx != 0xf0) { 127 ctx.outp[0] &= ~0x000000f0; 128 ctx.outp[0] |= i2cidx; 129 } 130 131 /* override dcb sorconf.link, based on what mxm data says */ 132 switch (ctx.desc.outp_type) { 133 case 0x00: /* Analog CRT */ 134 case 0x01: /* Analog TV/HDTV */ 135 break; 136 default: 137 link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30; 138 ctx.outp[1] &= ~0x00000030; 139 ctx.outp[1] |= link; 140 break; 141 } 142 143 /* we may need to fixup various other vbios tables based on what 144 * the descriptor says the connector type should be. 145 * 146 * in a lot of cases, the vbios tables will claim DVI-I is possible, 147 * and the mxm data says the connector is really HDMI. another 148 * common example is DP->eDP. 149 */ 150 conn = bios->data; 151 conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len); 152 type = conn[0]; 153 switch (ctx.desc.conn_type) { 154 case 0x01: /* LVDS */ 155 ctx.outp[1] |= 0x00000004; /* use_power_scripts */ 156 /* XXX: modify default link width in LVDS table */ 157 break; 158 case 0x02: /* HDMI */ 159 type = DCB_CONNECTOR_HDMI_1; 160 break; 161 case 0x03: /* DVI-D */ 162 type = DCB_CONNECTOR_DVI_D; 163 break; 164 case 0x0e: /* eDP, falls through to DPint */ 165 ctx.outp[1] |= 0x00010000; 166 case 0x07: /* DP internal, wtf is this?? HP8670w */ 167 ctx.outp[1] |= 0x00000004; /* use_power_scripts? */ 168 type = DCB_CONNECTOR_eDP; 169 break; 170 default: 171 break; 172 } 173 174 if (mxms_version(mxm) >= 0x0300) 175 conn[0] = type; 176 177 return 0; 178 } 179 180 static bool 181 mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info) 182 { 183 u64 desc = *(u64 *)data; 184 if ((desc & 0xf0) != 0xf0) 185 nv_info(mxm, "unmatched output device 0x%016llx\n", desc); 186 return true; 187 } 188 189 static void 190 mxm_dcb_sanitise(struct nvkm_mxm *mxm) 191 { 192 struct nvkm_bios *bios = nvkm_bios(mxm); 193 u8 ver, hdr, cnt, len; 194 u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len); 195 if (dcb == 0x0000 || ver != 0x40) { 196 nv_debug(mxm, "unsupported DCB version\n"); 197 return; 198 } 199 200 dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry); 201 mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL); 202 } 203 204 static int 205 nv50_mxm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, 206 struct nvkm_oclass *oclass, void *data, u32 size, 207 struct nvkm_object **pobject) 208 { 209 struct nv50_mxm_priv *priv; 210 int ret; 211 212 ret = nvkm_mxm_create(parent, engine, oclass, &priv); 213 *pobject = nv_object(priv); 214 if (ret) 215 return ret; 216 217 if (priv->base.action & MXM_SANITISE_DCB) 218 mxm_dcb_sanitise(&priv->base); 219 return 0; 220 } 221 222 struct nvkm_oclass 223 nv50_mxm_oclass = { 224 .handle = NV_SUBDEV(MXM, 0x50), 225 .ofuncs = &(struct nvkm_ofuncs) { 226 .ctor = nv50_mxm_ctor, 227 .dtor = _nvkm_mxm_dtor, 228 .init = _nvkm_mxm_init, 229 .fini = _nvkm_mxm_fini, 230 }, 231 }; 232