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