Home | History | Annotate | Line # | Download | only in mxm
      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