Skip to content

Commit

Permalink
drm/nouveau/disp: parse connector info directly in nouveau_connector.c
Browse files Browse the repository at this point in the history
Another case where we parsed vbios data to some structs, then again use
that info once to construct another set of data.  Skip the intermediate
step.

This is also slightly improved in that we can now use DCB 3.x connector
table info, which will allow NV4x to gain hotplug support, and to make
quirks for SPWG LVDS panels unnecessary.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
  • Loading branch information
Ben Skeggs committed Dec 21, 2011
1 parent f553b79 commit befb51e
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 328 deletions.
315 changes: 90 additions & 225 deletions drivers/gpu/drm/nouveau/nouveau_bios.c
Original file line number Diff line number Diff line change
Expand Up @@ -1100,13 +1100,9 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)

switch (cond) {
case 0:
{
struct dcb_connector_table_entry *ent =
&bios->dcb.connector.entry[dcb->connector];

if (ent->type != DCB_CONNECTOR_eDP)
entry = dcb_conn(dev, dcb->connector);
if (!entry || entry[0] != DCB_CONNECTOR_eDP)
iexec->execute = false;
}
break;
case 1:
case 2:
Expand Down Expand Up @@ -5782,164 +5778,6 @@ parse_dcb_gpio_table(struct nvbios *bios)
}
}

struct dcb_connector_table_entry *
nouveau_bios_connector_entry(struct drm_device *dev, int index)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
struct dcb_connector_table_entry *cte;

if (index >= bios->dcb.connector.entries)
return NULL;

cte = &bios->dcb.connector.entry[index];
if (cte->type == 0xff)
return NULL;

return cte;
}

static enum dcb_connector_type
divine_connector_type(struct nvbios *bios, int index)
{
struct dcb_table *dcb = &bios->dcb;
unsigned encoders = 0, type = DCB_CONNECTOR_NONE;
int i;

for (i = 0; i < dcb->entries; i++) {
if (dcb->entry[i].connector == index)
encoders |= (1 << dcb->entry[i].type);
}

if (encoders & (1 << OUTPUT_DP)) {
if (encoders & (1 << OUTPUT_TMDS))
type = DCB_CONNECTOR_DP;
else
type = DCB_CONNECTOR_eDP;
} else
if (encoders & (1 << OUTPUT_TMDS)) {
if (encoders & (1 << OUTPUT_ANALOG))
type = DCB_CONNECTOR_DVI_I;
else
type = DCB_CONNECTOR_DVI_D;
} else
if (encoders & (1 << OUTPUT_ANALOG)) {
type = DCB_CONNECTOR_VGA;
} else
if (encoders & (1 << OUTPUT_LVDS)) {
type = DCB_CONNECTOR_LVDS;
} else
if (encoders & (1 << OUTPUT_TV)) {
type = DCB_CONNECTOR_TV_0;
}

return type;
}

static void
apply_dcb_connector_quirks(struct nvbios *bios, int idx)
{
struct dcb_connector_table_entry *cte = &bios->dcb.connector.entry[idx];
struct drm_device *dev = bios->dev;

/* Gigabyte NX85T */
if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) {
if (cte->type == DCB_CONNECTOR_HDMI_1)
cte->type = DCB_CONNECTOR_DVI_I;
}

/* Gigabyte GV-NX86T512H */
if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) {
if (cte->type == DCB_CONNECTOR_HDMI_1)
cte->type = DCB_CONNECTOR_DVI_I;
}
}

static const u8 hpd_gpio[16] = {
0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60,
};

static void
parse_dcb_connector_table(struct nvbios *bios)
{
struct drm_device *dev = bios->dev;
struct dcb_connector_table *ct = &bios->dcb.connector;
struct dcb_connector_table_entry *cte;
uint8_t *conntab = &bios->data[bios->dcb.connector_table_ptr];
uint8_t *entry;
int i;

if (!bios->dcb.connector_table_ptr) {
NV_DEBUG_KMS(dev, "No DCB connector table present\n");
return;
}

NV_INFO(dev, "DCB connector table: VHER 0x%02x %d %d %d\n",
conntab[0], conntab[1], conntab[2], conntab[3]);
if ((conntab[0] != 0x30 && conntab[0] != 0x40) ||
(conntab[3] != 2 && conntab[3] != 4)) {
NV_ERROR(dev, " Unknown! Please report.\n");
return;
}

ct->entries = conntab[2];

entry = conntab + conntab[1];
cte = &ct->entry[0];
for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) {
cte->index = i;
if (conntab[3] == 2)
cte->entry = ROM16(entry[0]);
else
cte->entry = ROM32(entry[0]);

cte->type = (cte->entry & 0x000000ff) >> 0;
cte->index2 = (cte->entry & 0x00000f00) >> 8;

cte->gpio_tag = ffs((cte->entry & 0x07033000) >> 12);
cte->gpio_tag = hpd_gpio[cte->gpio_tag];

if (cte->type == 0xff)
continue;

apply_dcb_connector_quirks(bios, i);

NV_INFO(dev, " %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n",
i, cte->entry, cte->type, cte->index, cte->gpio_tag);

/* check for known types, fallback to guessing the type
* from attached encoders if we hit an unknown.
*/
switch (cte->type) {
case DCB_CONNECTOR_VGA:
case DCB_CONNECTOR_TV_0:
case DCB_CONNECTOR_TV_1:
case DCB_CONNECTOR_TV_3:
case DCB_CONNECTOR_DVI_I:
case DCB_CONNECTOR_DVI_D:
case DCB_CONNECTOR_LVDS:
case DCB_CONNECTOR_LVDS_SPWG:
case DCB_CONNECTOR_DP:
case DCB_CONNECTOR_eDP:
case DCB_CONNECTOR_HDMI_0:
case DCB_CONNECTOR_HDMI_1:
break;
default:
cte->type = divine_connector_type(bios, cte->index);
NV_WARN(dev, "unknown type, using 0x%02x\n", cte->type);
break;
}

if (nouveau_override_conntype) {
int type = divine_connector_type(bios, cte->index);
if (type != cte->type)
NV_WARN(dev, " -> type 0x%02x\n", cte->type);
}

}
}

void *
dcb_table(struct drm_device *dev)
{
Expand Down Expand Up @@ -6043,6 +5881,27 @@ dcb_outp_foreach(struct drm_device *dev, void *data,
return 0;
}

u8 *
dcb_conntab(struct drm_device *dev)
{
u8 *dcb = dcb_table(dev);
if (dcb && dcb[0] >= 0x30 && dcb[1] >= 0x16) {
u8 *conntab = ROMPTR(dev, dcb[0x14]);
if (conntab && conntab[0] >= 0x30 && conntab[0] <= 0x40)
return conntab;
}
return NULL;
}

u8 *
dcb_conn(struct drm_device *dev, u8 idx)
{
u8 *conntab = dcb_conntab(dev);
if (conntab && idx < conntab[2])
return conntab + conntab[1] + (idx * conntab[3]);
return NULL;
}

static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb)
{
struct dcb_entry *entry = &dcb->entry[dcb->entries];
Expand Down Expand Up @@ -6073,8 +5932,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
entry->type = conn & 0xf;
entry->i2c_index = (conn >> 4) & 0xf;
entry->heads = (conn >> 8) & 0xf;
if (dcb->version >= 0x40)
entry->connector = (conn >> 12) & 0xf;
entry->connector = (conn >> 12) & 0xf;
entry->bus = (conn >> 16) & 0xf;
entry->location = (conn >> 20) & 0x3;
entry->or = (conn >> 24) & 0xf;
Expand Down Expand Up @@ -6433,24 +6291,75 @@ parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp)
if (apply_dcb_encoder_quirks(dev, idx, &conn, &conf)) {
struct dcb_entry *entry = new_dcb_entry(dcb);

NV_TRACEWARN(dev, "DCB entry %02d: %08x %08x\n", idx, conn, conf);
NV_TRACEWARN(dev, "DCB outp %02d: %08x %08x\n", idx, conn, conf);

if (dcb->version >= 0x20)
ret = parse_dcb20_entry(dev, dcb, conn, conf, entry);
else
ret = parse_dcb15_entry(dev, dcb, conn, conf, entry);
if (!ret)
return 1; /* stop parsing */

/* Ignore the I2C index for on-chip TV-out, as there
* are cards with bogus values (nv31m in bug 23212),
* and it's otherwise useless.
*/
if (entry->type == OUTPUT_TV &&
entry->location == DCB_LOC_ON_CHIP)
entry->i2c_index = 0x0f;
}

return 0;
}

static void
dcb_fake_connectors(struct nvbios *bios)
{
struct dcb_table *dcbt = &bios->dcb;
u8 map[16] = { };
int i, idx = 0;

/* heuristic: if we ever get a non-zero connector field, assume
* that all the indices are valid and we don't need fake them.
*/
for (i = 0; i < dcbt->entries; i++) {
if (dcbt->entry[i].connector)
return;
}

/* no useful connector info available, we need to make it up
* ourselves. the rule here is: anything on the same i2c bus
* is considered to be on the same connector. any output
* without an associated i2c bus is assigned its own unique
* connector index.
*/
for (i = 0; i < dcbt->entries; i++) {
u8 i2c = dcbt->entry[i].i2c_index;
if (i2c == 0x0f) {
dcbt->entry[i].connector = idx++;
} else {
if (!map[i2c])
map[i2c] = ++idx;
dcbt->entry[i].connector = map[i2c] - 1;
}
}

/* if we created more than one connector, destroy the connector
* table - just in case it has random, rather than stub, entries.
*/
if (i > 1) {
u8 *conntab = dcb_conntab(bios->dev);
if (conntab)
conntab[0] = 0x00;
}
}

static int
parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
{
struct dcb_table *dcb = &bios->dcb;
u8 *dcbt;
u8 *dcbt, *conn;
int idx;

dcbt = dcb_table(dev);
if (!dcbt) {
Expand All @@ -6466,10 +6375,8 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
NV_TRACE(dev, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf);

dcb->version = dcbt[0];
if (dcb->version >= 0x30) {
if (dcb->version >= 0x30)
dcb->gpio_table_ptr = ROM16(dcbt[10]);
dcb->connector_table_ptr = ROM16(dcbt[20]);
}

dcb_outp_foreach(dev, NULL, parse_dcb_entry);

Expand All @@ -6483,61 +6390,21 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
if (!dcb->entries)
return -ENXIO;

parse_dcb_gpio_table(bios);
parse_dcb_connector_table(bios);
return 0;
}

static void
fixup_legacy_connector(struct nvbios *bios)
{
struct dcb_table *dcb = &bios->dcb;
int i, i2c, i2c_conn[DCB_MAX_NUM_I2C_ENTRIES] = { };

/*
* DCB 3.0 also has the table in most cases, but there are some cards
* where the table is filled with stub entries, and the DCB entriy
* indices are all 0. We don't need the connector indices on pre-G80
* chips (yet?) so limit the use to DCB 4.0 and above.
*/
if (dcb->version >= 0x40)
return;

dcb->connector.entries = 0;

/*
* No known connector info before v3.0, so make it up. the rule here
* is: anything on the same i2c bus is considered to be on the same
* connector. any output without an associated i2c bus is assigned
* its own unique connector index.
*/
for (i = 0; i < dcb->entries; i++) {
/*
* Ignore the I2C index for on-chip TV-out, as there
* are cards with bogus values (nv31m in bug 23212),
* and it's otherwise useless.
*/
if (dcb->entry[i].type == OUTPUT_TV &&
dcb->entry[i].location == DCB_LOC_ON_CHIP)
dcb->entry[i].i2c_index = 0xf;
i2c = dcb->entry[i].i2c_index;

if (i2c_conn[i2c]) {
dcb->entry[i].connector = i2c_conn[i2c] - 1;
continue;
/* dump connector table entries to log, if any exist */
idx = -1;
while ((conn = dcb_conn(dev, ++idx))) {
if (conn[0] != 0xff) {
NV_TRACE(dev, "DCB conn %02d: ", idx);
if (dcb_conntab(dev)[3] < 4)
printk("%04x\n", ROM16(conn[0]));
else
printk("%08x\n", ROM32(conn[0]));
}

dcb->entry[i].connector = dcb->connector.entries++;
if (i2c != 0xf)
i2c_conn[i2c] = dcb->connector.entries;
}
dcb_fake_connectors(bios);

/* Fake the connector table as well as just connector indices */
for (i = 0; i < dcb->connector.entries; i++) {
dcb->connector.entry[i].index = i;
dcb->connector.entry[i].type = divine_connector_type(bios, i);
dcb->connector.entry[i].gpio_tag = 0xff;
}
parse_dcb_gpio_table(bios);
return 0;
}

static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry)
Expand Down Expand Up @@ -6800,8 +6667,6 @@ nouveau_bios_init(struct drm_device *dev)
if (ret)
return ret;

fixup_legacy_connector(bios);

if (!bios->major_version) /* we don't run version 0 bios */
return 0;

Expand Down
Loading

0 comments on commit befb51e

Please sign in to comment.