From: Ben Skeggs Date: Tue, 6 Nov 2012 06:03:51 +0000 (+1000) Subject: drm/nouveau/bios: implement "full" BIT 'd' table (and subtable) parsing in core X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=65c78660fbbdac1e4f1d35fad1164d4536463189;p=openwrt%2Fstaging%2Fblogic.git drm/nouveau/bios: implement "full" BIT 'd' table (and subtable) parsing in core Signed-off-by: Ben Skeggs --- diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h index d682fb625833..0577c67612d4 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h @@ -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 diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h index 73b5e5d3e75a..6e54218b55fc 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h @@ -1,8 +1,34 @@ #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 diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c index 3cbc0f3e8d5e..663853bcca82 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c @@ -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); - return data; + *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)) - return data; + 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); +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 6be8c32f6e4c..ae168bbb86d8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -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; }