summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2011-11-18 10:23:59 +1000
committerBen Skeggs <bskeggs@redhat.com>2011-12-21 19:01:41 +1000
commitbefb51e9c97e783c86a1da27bdda3a638d2f02c7 (patch)
tree320f1a7a6d730decc566542e49e17c1a680956c4 /drivers
parentf553b79c03f0dbd52f6f03abe8233a2bef8cbd0d (diff)
downloadlinux-befb51e9c97e783c86a1da27bdda3a638d2f02c7.tar.gz
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: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c315
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h19
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c216
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c4
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvd0_display.c2
7 files changed, 240 insertions, 328 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 7922bb969d25..35b18f4f7c4e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -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 @@ no_table:
}
}
-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;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index a0e9c2c7ae8c..481e1f23f2f1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -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
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 576fb9af5d13..f6c8e1da4228 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -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;
+ }
- drm_connector_init(dev, connector, funcs, type);
- drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
+ /* Gigabyte GV-NX86T512H */
+ if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) {
+ if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
+ nv_connector->type = DCB_CONNECTOR_DVI_I;
+ }
+ } else {
+ nv_connector->type = DCB_CONNECTOR_NONE;
+ nv_connector->hpd = DCB_GPIO_UNUSED;
+ }
+
+ /* no vbios data, or an unknown dcb connector type - attempt to
+ * figure out something suitable ourselves
+ */
+ if (nv_connector->type == DCB_CONNECTOR_NONE) {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct dcb_table *dcbt = &dev_priv->vbios.dcb;
+ u32 encoders = 0;
+ int i;
+
+ for (i = 0; i < dcbt->entries; i++) {
+ if (dcbt->entry[i].connector == nv_connector->index)
+ encoders |= (1 << dcbt->entry[i].type);
+ }
- /* parse lvds table now, we depend on bios->fp.* values later */
- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
- bool dummy, is_24bit = false;
+ if (encoders & (1 << OUTPUT_DP)) {
+ if (encoders & (1 << OUTPUT_TMDS))
+ nv_connector->type = DCB_CONNECTOR_DP;
+ else
+ nv_connector->type = DCB_CONNECTOR_eDP;
+ } else
+ if (encoders & (1 << OUTPUT_TMDS)) {
+ if (encoders & (1 << OUTPUT_ANALOG))
+ nv_connector->type = DCB_CONNECTOR_DVI_I;
+ else
+ nv_connector->type = DCB_CONNECTOR_DVI_D;
+ } else
+ if (encoders & (1 << OUTPUT_ANALOG)) {
+ nv_connector->type = DCB_CONNECTOR_VGA;
+ } else
+ if (encoders & (1 << OUTPUT_LVDS)) {
+ nv_connector->type = DCB_CONNECTOR_LVDS;
+ } else
+ if (encoders & (1 << OUTPUT_TV)) {
+ nv_connector->type = DCB_CONNECTOR_TV_0;
+ }
+ }
- ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit);
+ type = drm_conntype_from_dcb(nv_connector->type);
+ if (type == DRM_MODE_CONNECTOR_LVDS) {
+ ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy);
if (ret) {
- NV_ERROR(dev, "Error parsing LVDS table, disabling "
- "LVDS\n");
- goto fail;
+ NV_ERROR(dev, "Error parsing LVDS table, disabling\n");
+ kfree(nv_connector);
+ return ERR_PTR(ret);
}
+
+ funcs = &nouveau_connector_funcs_lvds;
+ } else {
+ funcs = &nouveau_connector_funcs;
}
+ /* defaults, will get overridden in detect() */
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ drm_connector_init(dev, connector, funcs, type);
+ drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
+
/* Init DVI-I specific properties */
- if (dcb->type == DCB_CONNECTOR_DVI_I)
+ if (nv_connector->type == DCB_CONNECTOR_DVI_I)
drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
/* Add overscan compensation options to digital outputs */
if (disp->underscan_property &&
- (dcb->type == DCB_CONNECTOR_DVI_D ||
- dcb->type == DCB_CONNECTOR_DVI_I ||
- dcb->type == DCB_CONNECTOR_HDMI_0 ||
- dcb->type == DCB_CONNECTOR_HDMI_1 ||
- dcb->type == DCB_CONNECTOR_DP)) {
+ (nv_connector->type == DCB_CONNECTOR_DVI_D ||
+ nv_connector->type == DCB_CONNECTOR_DVI_I ||
+ nv_connector->type == DCB_CONNECTOR_HDMI_0 ||
+ nv_connector->type == DCB_CONNECTOR_HDMI_1 ||
+ nv_connector->type == DCB_CONNECTOR_DP)) {
drm_connector_attach_property(connector,
disp->underscan_property,
UNDERSCAN_OFF);
@@ -960,7 +1026,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
0);
}
- switch (dcb->type) {
+ switch (nv_connector->type) {
case DCB_CONNECTOR_VGA:
if (dev_priv->card_type >= NV_50) {
drm_connector_attach_property(connector,
@@ -994,8 +1060,8 @@ nouveau_connector_create(struct drm_device *dev, int index)
break;
}
- if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) {
- pgpio->irq_register(dev, nv_connector->dcb->gpio_tag,
+ if (nv_connector->hpd != DCB_GPIO_UNUSED && pgpio->irq_register) {
+ pgpio->irq_register(dev, nv_connector->hpd,
nouveau_connector_hotplug, connector);
connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -1004,15 +1070,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
}
drm_sysfs_connector_add(connector);
-
- dcb->drm = connector;
- return dcb->drm;
-
-fail:
- drm_connector_cleanup(connector);
- kfree(connector);
- return ERR_PTR(ret);
-
+ return connector;
}
static void
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 6468f5e67ccf..e4857021304c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -56,8 +56,10 @@ enum nouveau_dithering_depth {
struct nouveau_connector {
struct drm_connector base;
-
- struct dcb_connector_table_entry *dcb;
+ enum dcb_connector_type type;
+ u8 index;
+ u8 *dcb;
+ u8 hpd;
int dithering_mode;
int dithering_depth;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 07bac3602453..02b00c827da3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -587,7 +587,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
* we take during link training (DP_SET_POWER is one), we need
* to ignore them for the moment to avoid races.
*/
- pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false);
+ pgpio->irq_enable(dev, nv_connector->hpd, false);
/* enable down-spreading, if possible */
if (dp.table[1] >= 16) {
@@ -636,7 +636,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc);
/* re-enable hotplug detect */
- pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true);
+ pgpio->irq_enable(dev, nv_connector->hpd, true);
return true;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index a3d28a1e8bb2..9708b94a0a7b 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -241,10 +241,10 @@ nv50_display_init(struct drm_device *dev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct nouveau_connector *conn = nouveau_connector(connector);
- if (conn->dcb->gpio_tag == 0xff)
+ if (conn->hpd == DCB_GPIO_UNUSED)
continue;
- pgpio->irq_enable(dev, conn->dcb->gpio_tag, true);
+ pgpio->irq_enable(dev, conn->hpd, true);
}
ret = nv50_evo_init(dev);
@@ -581,7 +581,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb,
} else {
/* determine number of lvds links */
if (nv_connector && nv_connector->edid &&
- nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) {
+ nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
/* http://www.spwg.org */
if (((u8 *)nv_connector->edid)[121] == 2)
script |= 0x0100;
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index 89a31851e83a..d2ba2f07400b 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -1285,7 +1285,7 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
if (bios->fp.if_is_24bit)
or_config |= 0x0200;
} else {
- if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) {
+ if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
if (((u8 *)nv_connector->edid)[121] == 2)
or_config |= 0x0100;
} else