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 28 static u16 29 nvbios_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 59 static u16 60 nvbios_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 90 u16 91 nvbios_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 /* fall-through */ 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 137 u16 138 nvbios_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 152 static u16 153 nvbios_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 170 u16 171 nvbios_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 206 u16 207 nvbios_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 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