Commit befb51e9 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/disp: parse connector info directly in nouveau_connector.c



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: default avatarBen Skeggs <bskeggs@redhat.com>
parent f553b79c
......@@ -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:
......@@ -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)
{
......@@ -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];
......@@ -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;
......@@ -6433,7 +6291,7 @@ 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);
......@@ -6441,16 +6299,67 @@ parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp)
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) {
......@@ -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);
......@@ -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)
......@@ -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;
......
......@@ -58,6 +58,7 @@ enum dcb_gpio_tag {
DCB_GPIO_TVDAC1 = 0x2d,
DCB_GPIO_PWM_FAN = 0x9,
DCB_GPIO_FAN_SENSE = 0x3d,
DCB_GPIO_UNUSED = 0xff
};
struct dcb_gpio_entry {
......@@ -89,20 +90,6 @@ enum dcb_connector_type {
DCB_CONNECTOR_NONE = 0xff
};
struct dcb_connector_table_entry {
uint8_t index;
uint32_t entry;
enum dcb_connector_type type;
uint8_t index2;
uint8_t gpio_tag;
void *drm;
};
struct dcb_connector_table {
int entries;
struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
};
enum dcb_type {
OUTPUT_ANALOG = 0,
OUTPUT_TV = 1,
......@@ -161,8 +148,6 @@ struct dcb_table {
uint16_t gpio_table_ptr;
struct dcb_gpio_table gpio;
uint16_t connector_table_ptr;
struct dcb_connector_table connector;
};
enum nouveau_or {
......@@ -333,5 +318,7 @@ void *dcb_table(struct drm_device *);
u8 *dcb_outp(struct drm_device *, u8 idx);
int dcb_outp_foreach(struct drm_device *, void *data,
int (*)(struct drm_device *, void *, int idx, u8 *outp));
u8 *dcb_conntab(struct drm_device *);
u8 *dcb_conn(struct drm_device *, u8 idx);
#endif
......@@ -112,7 +112,7 @@ nouveau_connector_destroy(struct drm_connector *connector)
pgpio = &dev_priv->engine.gpio;
if (pgpio->irq_unregister) {
pgpio->irq_unregister(dev, nv_connector->dcb->gpio_tag,
pgpio->irq_unregister(dev, nv_connector->hpd,
nouveau_connector_hotplug, connector);
}
......@@ -218,7 +218,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
connector->interlace_allowed = true;
}
if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
drm_connector_property_set_value(connector,
dev->mode_config.dvi_i_subconnector_property,
nv_encoder->dcb->type == OUTPUT_TMDS ?
......@@ -401,7 +401,7 @@ nouveau_connector_force(struct drm_connector *connector)
struct nouveau_encoder *nv_encoder;
int type;
if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
if (connector->force == DRM_FORCE_ON_DIGITAL)
type = OUTPUT_TMDS;
else
......@@ -675,7 +675,7 @@ nouveau_connector_detect_depth(struct drm_connector *connector)
* know which if_is_24bit flag to check...
*/
if (nv_connector->edid &&
nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG)
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
duallink = ((u8 *)nv_connector->edid)[121] == 2;
else
duallink = mode->clock >= bios->fp.duallink_transition_clk;
......@@ -738,9 +738,9 @@ nouveau_connector_get_modes(struct drm_connector *connector)
if (nv_encoder->dcb->type == OUTPUT_TV)
ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG ||
nv_connector->dcb->type == DCB_CONNECTOR_eDP)
if (nv_connector->type == DCB_CONNECTOR_LVDS ||
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG ||
nv_connector->type == DCB_CONNECTOR_eDP)
ret += nouveau_connector_scaler_modes_add(connector);
return ret;
......@@ -857,6 +857,29 @@ nouveau_connector_funcs_lvds = {
.force = nouveau_connector_force
};
static int
drm_conntype_from_dcb(enum dcb_connector_type dcb)
{
switch (dcb) {
case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA;
case DCB_CONNECTOR_TV_0 :
case DCB_CONNECTOR_TV_1 :
case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV;
case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII;
case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID;
case DCB_CONNECTOR_LVDS :
case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS;
case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP;
case DCB_CONNECTOR_HDMI_0 :
case DCB_CONNECTOR_HDMI_1 : return DRM_MODE_CONNECTOR_HDMIA;
default:
break;
}
return DRM_MODE_CONNECTOR_Unknown;
}
struct drm_connector *
nouveau_connector_create(struct drm_device *dev, int index)
{
......@@ -865,90 +888,133 @@ nouveau_connector_create(struct drm_device *dev, int index)
struct nouveau_display_engine *disp = &dev_priv->engine.display;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nouveau_connector *nv_connector = NULL;
struct dcb_connector_table_entry *dcb = NULL;
struct drm_connector *connector;
int type, ret = 0;
bool dummy;
NV_DEBUG_KMS(dev, "\n");
if (index >= dev_priv->vbios.dcb.connector.entries)
return ERR_PTR(-EINVAL);
dcb = &dev_priv->vbios.dcb.connector.entry[index];
if (dcb->drm)
return dcb->drm;
switch (dcb->type) {
case DCB_CONNECTOR_VGA:
type = DRM_MODE_CONNECTOR_VGA;
break;
case DCB_CONNECTOR_TV_0:
case DCB_CONNECTOR_TV_1:
case DCB_CONNECTOR_TV_3:
type = DRM_MODE_CONNECTOR_TV;
break;
case DCB_CONNECTOR_DVI_I:
type = DRM_MODE_CONNECTOR_DVII;
break;
case DCB_CONNECTOR_DVI_D:
type = DRM_MODE_CONNECTOR_DVID;
break;
case DCB_CONNECTOR_HDMI_0:
case DCB_CONNECTOR_HDMI_1:
type = DRM_MODE_CONNECTOR_HDMIA;
break;
case DCB_CONNECTOR_LVDS:
case DCB_CONNECTOR_LVDS_SPWG:
type = DRM_MODE_CONNECTOR_LVDS;
funcs = &nouveau_connector_funcs_lvds;
break;
case DCB_CONNECTOR_DP:
type = DRM_MODE_CONNECTOR_DisplayPort;
break;
case DCB_CONNECTOR_eDP:
type = DRM_MODE_CONNECTOR_eDP;
break;
default:
NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type);
return ERR_PTR(-EINVAL);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
nv_connector = nouveau_connector(connector);
if (nv_connector->index == index)
return connector;
}
nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
if (!nv_connector)
return ERR_PTR(-ENOMEM);
nv_connector->dcb = dcb;
connector = &nv_connector->base;
nv_connector->index = index;
/* attempt to parse vbios connector type and hotplug gpio */
nv_connector->dcb = dcb_conn(dev, index);
if (nv_connector->dcb) {
static const u8 hpd[16] = {
0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60,
};
u32 entry = ROM16(nv_connector->dcb[0]);
if (dcb_conntab(dev)[3] >= 4)
entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
nv_connector->hpd = ffs((entry & 0x07033000) >> 12);
nv_connector->hpd = hpd[nv_connector->hpd];
nv_connector->type = nv_connector->dcb[0];
if (drm_conntype_from_dcb(nv_connector->type) ==
DRM_MODE_CONNECTOR_Unknown) {
NV_WARN(dev, "unknown connector type %02x\n",
nv_connector->type);
nv_connector->type = DCB_CONNECTOR_NONE;
}
/* defaults, will get overridden in detect() */
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
/* Gigabyte NX85T */
if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) {
if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
nv_connector->type = DCB_CONNECTOR_DVI_I;
}
</