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