linux/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c
<<
>>
Prefs
   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
  31struct context {
  32        u32 *outp;
  33        struct mxms_odev desc;
  34};
  35
  36static bool
  37mxm_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
  49static bool
  50mxm_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
  94static int
  95mxm_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                /* fall through */
 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
 177static bool
 178mxm_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
 187static void
 188mxm_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
 203int
 204nv50_mxm_new(struct nvkm_device *device, int index, struct nvkm_subdev **pmxm)
 205{
 206        struct nvkm_mxm *mxm;
 207        int ret;
 208
 209        ret = nvkm_mxm_new_(device, index, &mxm);
 210        if (mxm)
 211                *pmxm = &mxm->subdev;
 212        if (ret)
 213                return ret;
 214
 215        if (mxm->action & MXM_SANITISE_DCB)
 216                mxm_dcb_sanitise(mxm);
 217
 218        return 0;
 219}
 220