Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65c78660 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/bios: implement "full" BIT 'd' table (and subtable) parsing in core



Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 14464b8c
Loading
Loading
Loading
Loading
+0 −29
Original line number Diff line number Diff line
@@ -58,33 +58,4 @@ u16 dcb_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len);
int dcb_outp_foreach(struct nouveau_bios *, void *data, int (*exec)
		     (struct nouveau_bios *, void *, int index, u16 entry));


/* BIT 'U'/'d' table encoder subtables have hashes matching them to
 * a particular set of encoders.
 *
 * This function returns true if a particular DCB entry matches.
 */
static inline bool
dcb_hash_match(struct dcb_output *dcb, u32 hash)
{
	if ((hash & 0x000000f0) != (dcb->location << 4))
		return false;
	if ((hash & 0x0000000f) != dcb->type)
		return false;
	if (!(hash & (dcb->or << 16)))
		return false;

	switch (dcb->type) {
	case DCB_OUTPUT_TMDS:
	case DCB_OUTPUT_LVDS:
	case DCB_OUTPUT_DP:
		if (hash & 0x00c00000) {
			if (!(hash & (dcb->sorconf.link << 22)))
				return false;
		}
	default:
		return true;
	}
}

#endif
+29 −3
Original line number Diff line number Diff line
#ifndef __NVBIOS_DP_H__
#define __NVBIOS_DP_H__

u16 dp_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
u16 dp_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len);
u16 dp_outp_match(struct nouveau_bios *, struct dcb_output *, u8 *ver, u8 *len);
struct nvbios_dpout {
	u16 type;
	u16 mask;
	u8  flags;
	u32 script[5];
	u32 lnkcmp;
};

u16 nvbios_dpout_parse(struct nouveau_bios *, u8 idx,
		       u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		       struct nvbios_dpout *);
u16 nvbios_dpout_match(struct nouveau_bios *, u16 type, u16 mask,
		       u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		       struct nvbios_dpout *);

struct nvbios_dpcfg {
	u8 drv;
	u8 pre;
	u8 unk;
};

u16
nvbios_dpcfg_parse(struct nouveau_bios *, u16 outp, u8 idx,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		   struct nvbios_dpcfg *);
u16
nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 un, u8 vs, u8 pe,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		   struct nvbios_dpcfg *);

#endif
+155 −27
Original line number Diff line number Diff line
@@ -25,23 +25,29 @@

#include "subdev/bios.h"
#include "subdev/bios/bit.h"
#include "subdev/bios/dcb.h"
#include "subdev/bios/dp.h"

u16
dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
static u16
nvbios_dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
	struct bit_entry bit_d;
	struct bit_entry d;

	if (!bit_entry(bios, 'd', &bit_d)) {
		if (bit_d.version == 1) {
			u16 data = nv_ro16(bios, bit_d.offset);
	if (!bit_entry(bios, 'd', &d)) {
		if (d.version == 1 && d.length >= 2) {
			u16 data = nv_ro16(bios, d.offset);
			if (data) {
				*ver = nv_ro08(bios, data + 0);
				*hdr = nv_ro08(bios, data + 1);
				*len = nv_ro08(bios, data + 2);
				*cnt = nv_ro08(bios, data + 3);
				*ver = nv_ro08(bios, data + 0x00);
				switch (*ver) {
				case 0x21:
				case 0x30:
				case 0x40:
					*hdr = nv_ro08(bios, data + 0x01);
					*len = nv_ro08(bios, data + 0x02);
					*cnt = nv_ro08(bios, data + 0x03);
					return data;
				default:
					break;
				}
			}
		}
	}
@@ -49,28 +55,150 @@ dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
	return 0x0000;
}

static u16
nvbios_dpout_entry(struct nouveau_bios *bios, u8 idx,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
	u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
	if (data && idx < *cnt) {
		u16 outp = nv_ro16(bios, data + *hdr + idx * *len);
		switch (*ver * !!outp) {
		case 0x21:
		case 0x30:
			*hdr = nv_ro08(bios, data + 0x04);
			*len = nv_ro08(bios, data + 0x05);
			*cnt = nv_ro08(bios, outp + 0x04);
			break;
		case 0x40:
			*hdr = nv_ro08(bios, data + 0x04);
			*cnt = 0;
			*len = 0;
			break;
		default:
			break;
		}
		return outp;
	}
	*ver = 0x00;
	return 0x0000;
}

u16
dp_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
nvbios_dpout_parse(struct nouveau_bios *bios, u8 idx,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		   struct nvbios_dpout *info)
{
	u8  hdr, cnt;
	u16 table = dp_table(bios, ver, &hdr, &cnt, len);
	if (table && idx < cnt)
		return nv_ro16(bios, table + hdr + (idx * *len));
	return 0xffff;
	u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
	if (data && *ver) {
		info->type = nv_ro16(bios, data + 0x00);
		info->mask = nv_ro16(bios, data + 0x02);
		switch (*ver) {
		case 0x21:
		case 0x30:
			info->flags     = nv_ro08(bios, data + 0x05);
			info->script[0] = nv_ro16(bios, data + 0x06);
			info->script[1] = nv_ro16(bios, data + 0x08);
			info->lnkcmp    = nv_ro16(bios, data + 0x0a);
			info->script[2] = nv_ro16(bios, data + 0x0c);
			info->script[3] = nv_ro16(bios, data + 0x0e);
			info->script[4] = nv_ro16(bios, data + 0x10);
			break;
		case 0x40:
			info->flags     = nv_ro08(bios, data + 0x04);
			info->script[0] = nv_ro16(bios, data + 0x05);
			info->script[1] = nv_ro16(bios, data + 0x07);
			info->lnkcmp    = nv_ro16(bios, data + 0x09);
			info->script[2] = nv_ro16(bios, data + 0x0b);
			info->script[3] = nv_ro16(bios, data + 0x0d);
			info->script[4] = nv_ro16(bios, data + 0x0f);
			break;
		default:
			data = 0x0000;
			break;
		}
	}
	return data;
}

u16
dp_outp_match(struct nouveau_bios *bios, struct dcb_output *outp,
	      u8 *ver, u8 *len)
nvbios_dpout_match(struct nouveau_bios *bios, u16 type, u16 mask,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		   struct nvbios_dpout *info)
{
	u8  idx = 0;
	u16 data;
	while ((data = dp_outp(bios, idx++, ver, len)) != 0xffff) {
		if (data) {
			u32 hash = nv_ro32(bios, data);
			if (dcb_hash_match(outp, hash))
	u16 data, idx = 0;
	while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
		if (data && info->type == type) {
			if ((info->mask & mask) == mask)
				break;
		}
	}
	return data;
}

static u16
nvbios_dpcfg_entry(struct nouveau_bios *bios, u16 outp, u8 idx,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
	if (*ver >= 0x40) {
		outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
		*hdr = *hdr + (*len * * cnt);
		*len = nv_ro08(bios, outp + 0x06);
		*cnt = nv_ro08(bios, outp + 0x07);
	}

	if (idx < *cnt)
		return outp + *hdr + (idx * *len);

	return 0x0000;
}

u16
nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		   struct nvbios_dpcfg *info)
{
	u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
	if (data) {
		switch (*ver) {
		case 0x21:
			info->drv = nv_ro08(bios, data + 0x02);
			info->pre = nv_ro08(bios, data + 0x03);
			info->unk = nv_ro08(bios, data + 0x04);
			break;
		case 0x30:
		case 0x40:
			info->drv = nv_ro08(bios, data + 0x01);
			info->pre = nv_ro08(bios, data + 0x02);
			info->unk = nv_ro08(bios, data + 0x03);
			break;
		default:
			data = 0x0000;
			break;
		}
	}
	return data;
}

u16
nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe,
		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
		   struct nvbios_dpcfg *info)
{
	u8 idx = 0xff;
	u16 data;

	if (*ver >= 0x30) {
		const u8 vsoff[] = { 0, 4, 7, 9 };
		idx = (un * 10) + vsoff[vs] + pe;
	} else {
		while ((data = nvbios_dpcfg_entry(bios, outp, idx,
						  ver, hdr, cnt, len))) {
			if (nv_ro08(bios, data + 0x00) == vs &&
			    nv_ro08(bios, data + 0x01) == pe)
				break;
			idx++;
		}
	}

	return nvbios_dpcfg_parse(bios, outp, pe, ver, hdr, cnt, len, info);
}
+8 −5
Original line number Diff line number Diff line
@@ -743,9 +743,10 @@ static void
init_dp_condition(struct nvbios_init *init)
{
	struct nouveau_bios *bios = init->bios;
	struct nvbios_dpout info;
	u8  cond = nv_ro08(bios, init->offset + 1);
	u8  unkn = nv_ro08(bios, init->offset + 2);
	u8  ver, len;
	u8  ver, hdr, cnt, len;
	u16 data;

	trace("DP_CONDITION\t0x%02x 0x%02x\n", cond, unkn);
@@ -759,10 +760,12 @@ init_dp_condition(struct nvbios_init *init)
	case 1:
	case 2:
		if ( init->outp &&
		    (data = dp_outp_match(bios, init->outp, &ver, &len))) {
			if (ver <= 0x40 && !(nv_ro08(bios, data + 5) & cond))
				init_exec_set(init, false);
			if (ver == 0x40 && !(nv_ro08(bios, data + 4) & cond))
		    (data = nvbios_dpout_match(bios, DCB_OUTPUT_DP,
					       (init->outp->or << 0) |
					       (init->outp->sorconf.link << 6),
					       &ver, &hdr, &cnt, &len, &info)))
		{
			if (!(info.flags & cond))
				init_exec_set(init, false);
			break;
		}