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