linux/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 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 <subdev/bios.h>
  25#include <subdev/bios/bit.h>
  26#include <subdev/bios/dp.h>
  27
  28u16
  29nvbios_dp_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
  30{
  31        struct bit_entry d;
  32
  33        if (!bit_entry(bios, 'd', &d)) {
  34                if (d.version == 1 && d.length >= 2) {
  35                        u16 data = nvbios_rd16(bios, d.offset);
  36                        if (data) {
  37                                *ver = nvbios_rd08(bios, data + 0x00);
  38                                switch (*ver) {
  39                                case 0x20:
  40                                case 0x21:
  41                                case 0x30:
  42                                case 0x40:
  43                                case 0x41:
  44                                case 0x42:
  45                                        *hdr = nvbios_rd08(bios, data + 0x01);
  46                                        *len = nvbios_rd08(bios, data + 0x02);
  47                                        *cnt = nvbios_rd08(bios, data + 0x03);
  48                                        return data;
  49                                default:
  50                                        break;
  51                                }
  52                        }
  53                }
  54        }
  55
  56        return 0x0000;
  57}
  58
  59static u16
  60nvbios_dpout_entry(struct nvkm_bios *bios, u8 idx,
  61                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
  62{
  63        u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
  64        if (data && idx < *cnt) {
  65                u16 outp = nvbios_rd16(bios, data + *hdr + idx * *len);
  66                switch (*ver * !!outp) {
  67                case 0x20:
  68                case 0x21:
  69                case 0x30:
  70                        *hdr = nvbios_rd08(bios, data + 0x04);
  71                        *len = nvbios_rd08(bios, data + 0x05);
  72                        *cnt = nvbios_rd08(bios, outp + 0x04);
  73                        break;
  74                case 0x40:
  75                case 0x41:
  76                case 0x42:
  77                        *hdr = nvbios_rd08(bios, data + 0x04);
  78                        *cnt = 0;
  79                        *len = 0;
  80                        break;
  81                default:
  82                        break;
  83                }
  84                return outp;
  85        }
  86        *ver = 0x00;
  87        return 0x0000;
  88}
  89
  90u16
  91nvbios_dpout_parse(struct nvkm_bios *bios, u8 idx,
  92                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
  93                   struct nvbios_dpout *info)
  94{
  95        u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
  96        memset(info, 0x00, sizeof(*info));
  97        if (data && *ver) {
  98                info->type = nvbios_rd16(bios, data + 0x00);
  99                info->mask = nvbios_rd16(bios, data + 0x02);
 100                switch (*ver) {
 101                case 0x20:
 102                        info->mask |= 0x00c0; /* match any link */
 103                        fallthrough;
 104                case 0x21:
 105                case 0x30:
 106                        info->flags     = nvbios_rd08(bios, data + 0x05);
 107                        info->script[0] = nvbios_rd16(bios, data + 0x06);
 108                        info->script[1] = nvbios_rd16(bios, data + 0x08);
 109                        if (*len >= 0x0c)
 110                                info->lnkcmp    = nvbios_rd16(bios, data + 0x0a);
 111                        if (*len >= 0x0f) {
 112                                info->script[2] = nvbios_rd16(bios, data + 0x0c);
 113                                info->script[3] = nvbios_rd16(bios, data + 0x0e);
 114                        }
 115                        if (*len >= 0x11)
 116                                info->script[4] = nvbios_rd16(bios, data + 0x10);
 117                        break;
 118                case 0x40:
 119                case 0x41:
 120                case 0x42:
 121                        info->flags     = nvbios_rd08(bios, data + 0x04);
 122                        info->script[0] = nvbios_rd16(bios, data + 0x05);
 123                        info->script[1] = nvbios_rd16(bios, data + 0x07);
 124                        info->lnkcmp    = nvbios_rd16(bios, data + 0x09);
 125                        info->script[2] = nvbios_rd16(bios, data + 0x0b);
 126                        info->script[3] = nvbios_rd16(bios, data + 0x0d);
 127                        info->script[4] = nvbios_rd16(bios, data + 0x0f);
 128                        break;
 129                default:
 130                        data = 0x0000;
 131                        break;
 132                }
 133        }
 134        return data;
 135}
 136
 137u16
 138nvbios_dpout_match(struct nvkm_bios *bios, u16 type, u16 mask,
 139                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
 140                   struct nvbios_dpout *info)
 141{
 142        u16 data, idx = 0;
 143        while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
 144                if (data && info->type == type) {
 145                        if ((info->mask & mask) == mask)
 146                                break;
 147                }
 148        }
 149        return data;
 150}
 151
 152static u16
 153nvbios_dpcfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
 154                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
 155{
 156        if (*ver >= 0x40) {
 157                outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
 158                *hdr = *hdr + (*len * * cnt);
 159                *len = nvbios_rd08(bios, outp + 0x06);
 160                *cnt = nvbios_rd08(bios, outp + 0x07) *
 161                       nvbios_rd08(bios, outp + 0x05);
 162        }
 163
 164        if (idx < *cnt)
 165                return outp + *hdr + (idx * *len);
 166
 167        return 0x0000;
 168}
 169
 170u16
 171nvbios_dpcfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
 172                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
 173                   struct nvbios_dpcfg *info)
 174{
 175        u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
 176        memset(info, 0x00, sizeof(*info));
 177        if (data) {
 178                switch (*ver) {
 179                case 0x20:
 180                case 0x21:
 181                        info->dc    = nvbios_rd08(bios, data + 0x02);
 182                        info->pe    = nvbios_rd08(bios, data + 0x03);
 183                        info->tx_pu = nvbios_rd08(bios, data + 0x04);
 184                        break;
 185                case 0x30:
 186                case 0x40:
 187                case 0x41:
 188                        info->pc    = nvbios_rd08(bios, data + 0x00);
 189                        info->dc    = nvbios_rd08(bios, data + 0x01);
 190                        info->pe    = nvbios_rd08(bios, data + 0x02);
 191                        info->tx_pu = nvbios_rd08(bios, data + 0x03);
 192                        break;
 193                case 0x42:
 194                        info->dc    = nvbios_rd08(bios, data + 0x00);
 195                        info->pe    = nvbios_rd08(bios, data + 0x01);
 196                        info->tx_pu = nvbios_rd08(bios, data + 0x02);
 197                        break;
 198                default:
 199                        data = 0x0000;
 200                        break;
 201                }
 202        }
 203        return data;
 204}
 205
 206u16
 207nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
 208                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
 209                   struct nvbios_dpcfg *info)
 210{
 211        u8 idx = 0xff;
 212        u16 data;
 213
 214        if (*ver >= 0x30) {
 215                static const u8 vsoff[] = { 0, 4, 7, 9 };
 216                idx = (pc * 10) + vsoff[vs] + pe;
 217                if (*ver >= 0x40 && *ver <= 0x41 && *hdr >= 0x12)
 218                        idx += nvbios_rd08(bios, outp + 0x11) * 40;
 219                else
 220                if (*ver >= 0x42)
 221                        idx += nvbios_rd08(bios, outp + 0x11) * 10;
 222        } else {
 223                while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
 224                                                  ver, hdr, cnt, len))) {
 225                        if (nvbios_rd08(bios, data + 0x00) == vs &&
 226                            nvbios_rd08(bios, data + 0x01) == pe)
 227                                break;
 228                }
 229        }
 230
 231        return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
 232}
 233