Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2011 Red Hat Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Ben Skeggs
0023  */
0024 #include "mxms.h"
0025 
0026 #include <subdev/bios.h>
0027 #include <subdev/bios/conn.h>
0028 #include <subdev/bios/dcb.h>
0029 #include <subdev/bios/mxm.h>
0030 
0031 struct context {
0032     u32 *outp;
0033     struct mxms_odev desc;
0034 };
0035 
0036 static bool
0037 mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info)
0038 {
0039     struct context *ctx = info;
0040     struct mxms_odev desc;
0041 
0042     mxms_output_device(mxm, data, &desc);
0043     if (desc.outp_type == 2 &&
0044         desc.dig_conn == ctx->desc.dig_conn)
0045         return false;
0046     return true;
0047 }
0048 
0049 static bool
0050 mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info)
0051 {
0052     struct nvkm_bios *bios = mxm->subdev.device->bios;
0053     struct context *ctx = info;
0054     u64 desc = *(u64 *)data;
0055 
0056     mxms_output_device(mxm, data, &ctx->desc);
0057 
0058     /* match dcb encoder type to mxm-ods device type */
0059     if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type)
0060         return true;
0061 
0062     /* digital output, have some extra stuff to match here, there's a
0063      * table in the vbios that provides a mapping from the mxm digital
0064      * connection enum values to SOR/link
0065      */
0066     if ((desc & 0x00000000000000f0) >= 0x20) {
0067         /* check against sor index */
0068         u8 link = mxm_sor_map(bios, ctx->desc.dig_conn);
0069         if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24)
0070             return true;
0071 
0072         /* check dcb entry has a compatible link field */
0073         link = (link & 0x30) >> 4;
0074         if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link)
0075             return true;
0076     }
0077 
0078     /* mark this descriptor accounted for by setting invalid device type,
0079      * except of course some manufactures don't follow specs properly and
0080      * we need to avoid killing off the TMDS function on DP connectors
0081      * if MXM-SIS is missing an entry for it.
0082      */
0083     data[0] &= ~0xf0;
0084     if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 &&
0085         mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) {
0086         data[0] |= 0x20; /* modify descriptor to match TMDS now */
0087     } else {
0088         data[0] |= 0xf0;
0089     }
0090 
0091     return false;
0092 }
0093 
0094 static int
0095 mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb)
0096 {
0097     struct nvkm_mxm *mxm = data;
0098     struct context ctx = { .outp = (u32 *)(bios->data + pdcb) };
0099     u8 type, i2cidx, link, ver, len;
0100     u8 *conn;
0101 
0102     /* look for an output device structure that matches this dcb entry.
0103      * if one isn't found, disable it.
0104      */
0105     if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) {
0106         nvkm_debug(&mxm->subdev, "disable %d: %08x %08x\n",
0107                idx, ctx.outp[0], ctx.outp[1]);
0108         ctx.outp[0] |= 0x0000000f;
0109         return 0;
0110     }
0111 
0112     /* modify the output's ddc/aux port, there's a pointer to a table
0113      * with the mapping from mxm ddc/aux port to dcb i2c_index in the
0114      * vbios mxm table
0115      */
0116     i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port);
0117     if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP)
0118         i2cidx = (i2cidx & 0x0f) << 4;
0119     else
0120         i2cidx = (i2cidx & 0xf0);
0121 
0122     if (i2cidx != 0xf0) {
0123         ctx.outp[0] &= ~0x000000f0;
0124         ctx.outp[0] |= i2cidx;
0125     }
0126 
0127     /* override dcb sorconf.link, based on what mxm data says */
0128     switch (ctx.desc.outp_type) {
0129     case 0x00: /* Analog CRT */
0130     case 0x01: /* Analog TV/HDTV */
0131         break;
0132     default:
0133         link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30;
0134         ctx.outp[1] &= ~0x00000030;
0135         ctx.outp[1] |= link;
0136         break;
0137     }
0138 
0139     /* we may need to fixup various other vbios tables based on what
0140      * the descriptor says the connector type should be.
0141      *
0142      * in a lot of cases, the vbios tables will claim DVI-I is possible,
0143      * and the mxm data says the connector is really HDMI.  another
0144      * common example is DP->eDP.
0145      */
0146     conn  = bios->data;
0147     conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
0148     type  = conn[0];
0149     switch (ctx.desc.conn_type) {
0150     case 0x01: /* LVDS */
0151         ctx.outp[1] |= 0x00000004; /* use_power_scripts */
0152         /* XXX: modify default link width in LVDS table */
0153         break;
0154     case 0x02: /* HDMI */
0155         type = DCB_CONNECTOR_HDMI_1;
0156         break;
0157     case 0x03: /* DVI-D */
0158         type = DCB_CONNECTOR_DVI_D;
0159         break;
0160     case 0x0e: /* eDP, falls through to DPint */
0161         ctx.outp[1] |= 0x00010000;
0162         fallthrough;
0163     case 0x07: /* DP internal, wtf is this?? HP8670w */
0164         ctx.outp[1] |= 0x00000004; /* use_power_scripts? */
0165         type = DCB_CONNECTOR_eDP;
0166         break;
0167     default:
0168         break;
0169     }
0170 
0171     if (mxms_version(mxm) >= 0x0300)
0172         conn[0] = type;
0173 
0174     return 0;
0175 }
0176 
0177 static bool
0178 mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info)
0179 {
0180     struct nvkm_subdev *subdev = &mxm->subdev;
0181     u64 desc = *(u64 *)data;
0182     if ((desc & 0xf0) != 0xf0)
0183         nvkm_info(subdev, "unmatched output device %016llx\n", desc);
0184     return true;
0185 }
0186 
0187 static void
0188 mxm_dcb_sanitise(struct nvkm_mxm *mxm)
0189 {
0190     struct nvkm_subdev *subdev = &mxm->subdev;
0191     struct nvkm_bios *bios = subdev->device->bios;
0192     u8  ver, hdr, cnt, len;
0193     u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len);
0194     if (dcb == 0x0000 || (ver != 0x40 && ver != 0x41)) {
0195         nvkm_warn(subdev, "unsupported DCB version\n");
0196         return;
0197     }
0198 
0199     dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry);
0200     mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL);
0201 }
0202 
0203 int
0204 nv50_mxm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
0205          struct nvkm_subdev **pmxm)
0206 {
0207     struct nvkm_mxm *mxm;
0208     int ret;
0209 
0210     ret = nvkm_mxm_new_(device, type, inst, &mxm);
0211     if (mxm)
0212         *pmxm = &mxm->subdev;
0213     if (ret)
0214         return ret;
0215 
0216     if (mxm->action & MXM_SANITISE_DCB)
0217         mxm_dcb_sanitise(mxm);
0218 
0219     return 0;
0220 }