Home | History | Annotate | Line # | Download | only in bios
      1 /*	$NetBSD: nouveau_nvkm_subdev_bios_dp.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2012 Red Hat Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: Ben Skeggs
     25  */
     26 #include <sys/cdefs.h>
     27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_bios_dp.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $");
     28 
     29 #include <subdev/bios.h>
     30 #include <subdev/bios/bit.h>
     31 #include <subdev/bios/dp.h>
     32 
     33 u16
     34 nvbios_dp_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
     35 {
     36 	struct bit_entry d;
     37 
     38 	if (!bit_entry(bios, 'd', &d)) {
     39 		if (d.version == 1 && d.length >= 2) {
     40 			u16 data = nvbios_rd16(bios, d.offset);
     41 			if (data) {
     42 				*ver = nvbios_rd08(bios, data + 0x00);
     43 				switch (*ver) {
     44 				case 0x20:
     45 				case 0x21:
     46 				case 0x30:
     47 				case 0x40:
     48 				case 0x41:
     49 				case 0x42:
     50 					*hdr = nvbios_rd08(bios, data + 0x01);
     51 					*len = nvbios_rd08(bios, data + 0x02);
     52 					*cnt = nvbios_rd08(bios, data + 0x03);
     53 					return data;
     54 				default:
     55 					break;
     56 				}
     57 			}
     58 		}
     59 	}
     60 
     61 	return 0x0000;
     62 }
     63 
     64 static u16
     65 nvbios_dpout_entry(struct nvkm_bios *bios, u8 idx,
     66 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
     67 {
     68 	u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
     69 	if (data && idx < *cnt) {
     70 		u16 outp = nvbios_rd16(bios, data + *hdr + idx * *len);
     71 		switch (*ver * !!outp) {
     72 		case 0x20:
     73 		case 0x21:
     74 		case 0x30:
     75 			*hdr = nvbios_rd08(bios, data + 0x04);
     76 			*len = nvbios_rd08(bios, data + 0x05);
     77 			*cnt = nvbios_rd08(bios, outp + 0x04);
     78 			break;
     79 		case 0x40:
     80 		case 0x41:
     81 		case 0x42:
     82 			*hdr = nvbios_rd08(bios, data + 0x04);
     83 			*cnt = 0;
     84 			*len = 0;
     85 			break;
     86 		default:
     87 			break;
     88 		}
     89 		return outp;
     90 	}
     91 	*ver = 0x00;
     92 	return 0x0000;
     93 }
     94 
     95 u16
     96 nvbios_dpout_parse(struct nvkm_bios *bios, u8 idx,
     97 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
     98 		   struct nvbios_dpout *info)
     99 {
    100 	u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
    101 	memset(info, 0x00, sizeof(*info));
    102 	if (data && *ver) {
    103 		info->type = nvbios_rd16(bios, data + 0x00);
    104 		info->mask = nvbios_rd16(bios, data + 0x02);
    105 		switch (*ver) {
    106 		case 0x20:
    107 			info->mask |= 0x00c0; /* match any link */
    108 			/* fall-through */
    109 		case 0x21:
    110 		case 0x30:
    111 			info->flags     = nvbios_rd08(bios, data + 0x05);
    112 			info->script[0] = nvbios_rd16(bios, data + 0x06);
    113 			info->script[1] = nvbios_rd16(bios, data + 0x08);
    114 			if (*len >= 0x0c)
    115 				info->lnkcmp    = nvbios_rd16(bios, data + 0x0a);
    116 			if (*len >= 0x0f) {
    117 				info->script[2] = nvbios_rd16(bios, data + 0x0c);
    118 				info->script[3] = nvbios_rd16(bios, data + 0x0e);
    119 			}
    120 			if (*len >= 0x11)
    121 				info->script[4] = nvbios_rd16(bios, data + 0x10);
    122 			break;
    123 		case 0x40:
    124 		case 0x41:
    125 		case 0x42:
    126 			info->flags     = nvbios_rd08(bios, data + 0x04);
    127 			info->script[0] = nvbios_rd16(bios, data + 0x05);
    128 			info->script[1] = nvbios_rd16(bios, data + 0x07);
    129 			info->lnkcmp    = nvbios_rd16(bios, data + 0x09);
    130 			info->script[2] = nvbios_rd16(bios, data + 0x0b);
    131 			info->script[3] = nvbios_rd16(bios, data + 0x0d);
    132 			info->script[4] = nvbios_rd16(bios, data + 0x0f);
    133 			break;
    134 		default:
    135 			data = 0x0000;
    136 			break;
    137 		}
    138 	}
    139 	return data;
    140 }
    141 
    142 u16
    143 nvbios_dpout_match(struct nvkm_bios *bios, u16 type, u16 mask,
    144 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
    145 		   struct nvbios_dpout *info)
    146 {
    147 	u16 data, idx = 0;
    148 	while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
    149 		if (data && info->type == type) {
    150 			if ((info->mask & mask) == mask)
    151 				break;
    152 		}
    153 	}
    154 	return data;
    155 }
    156 
    157 static u16
    158 nvbios_dpcfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
    159 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
    160 {
    161 	if (*ver >= 0x40) {
    162 		outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
    163 		*hdr = *hdr + (*len * * cnt);
    164 		*len = nvbios_rd08(bios, outp + 0x06);
    165 		*cnt = nvbios_rd08(bios, outp + 0x07) *
    166 		       nvbios_rd08(bios, outp + 0x05);
    167 	}
    168 
    169 	if (idx < *cnt)
    170 		return outp + *hdr + (idx * *len);
    171 
    172 	return 0x0000;
    173 }
    174 
    175 u16
    176 nvbios_dpcfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
    177 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
    178 		   struct nvbios_dpcfg *info)
    179 {
    180 	u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
    181 	memset(info, 0x00, sizeof(*info));
    182 	if (data) {
    183 		switch (*ver) {
    184 		case 0x20:
    185 		case 0x21:
    186 			info->dc    = nvbios_rd08(bios, data + 0x02);
    187 			info->pe    = nvbios_rd08(bios, data + 0x03);
    188 			info->tx_pu = nvbios_rd08(bios, data + 0x04);
    189 			break;
    190 		case 0x30:
    191 		case 0x40:
    192 		case 0x41:
    193 			info->pc    = nvbios_rd08(bios, data + 0x00);
    194 			info->dc    = nvbios_rd08(bios, data + 0x01);
    195 			info->pe    = nvbios_rd08(bios, data + 0x02);
    196 			info->tx_pu = nvbios_rd08(bios, data + 0x03);
    197 			break;
    198 		case 0x42:
    199 			info->dc    = nvbios_rd08(bios, data + 0x00);
    200 			info->pe    = nvbios_rd08(bios, data + 0x01);
    201 			info->tx_pu = nvbios_rd08(bios, data + 0x02);
    202 			break;
    203 		default:
    204 			data = 0x0000;
    205 			break;
    206 		}
    207 	}
    208 	return data;
    209 }
    210 
    211 u16
    212 nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
    213 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
    214 		   struct nvbios_dpcfg *info)
    215 {
    216 	u8 idx = 0xff;
    217 	u16 data;
    218 
    219 	if (*ver >= 0x30) {
    220 		static const u8 vsoff[] = { 0, 4, 7, 9 };
    221 		idx = (pc * 10) + vsoff[vs] + pe;
    222 		if (*ver >= 0x40 && *ver <= 0x41 && *hdr >= 0x12)
    223 			idx += nvbios_rd08(bios, outp + 0x11) * 40;
    224 		else
    225 		if (*ver >= 0x42)
    226 			idx += nvbios_rd08(bios, outp + 0x11) * 10;
    227 	} else {
    228 		while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
    229 						  ver, hdr, cnt, len))) {
    230 			if (nvbios_rd08(bios, data + 0x00) == vs &&
    231 			    nvbios_rd08(bios, data + 0x01) == pe)
    232 				break;
    233 		}
    234 	}
    235 
    236 	return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
    237 }
    238