Commit 6b5a81a2 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/bios: start refactoring dcb routines



This primary reason for this was mostly to avoid duplication of some of
this stuff by the MXM-SIS parser.  However, some other cleanups will also
follow this as a result.

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 0f8067c7
Loading
Loading
Loading
Loading
+149 −121
Original line number Diff line number Diff line
@@ -6048,6 +6048,109 @@ parse_dcb_connector_table(struct nvbios *bios)
	}
}

void *
dcb_table(struct drm_device *dev)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	u8 *dcb = NULL;

	if (dev_priv->card_type > NV_04)
		dcb = ROMPTR(dev, dev_priv->vbios.data[0x36]);
	if (!dcb) {
		NV_WARNONCE(dev, "No DCB data found in VBIOS\n");
		return NULL;
	}

	if (dcb[0] >= 0x41) {
		NV_WARNONCE(dev, "DCB version 0x%02x unknown\n", dcb[0]);
		return NULL;
	} else
	if (dcb[0] >= 0x30) {
		if (ROM32(dcb[6]) == 0x4edcbdcb)
			return dcb;
	} else
	if (dcb[0] >= 0x20) {
		if (ROM32(dcb[4]) == 0x4edcbdcb)
			return dcb;
	} else
	if (dcb[0] >= 0x15) {
		if (!memcmp(&dcb[-7], "DEV_REC", 7))
			return dcb;
	} else {
		/*
		 * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but
		 * always has the same single (crt) entry, even when tv-out
		 * present, so the conclusion is this version cannot really
		 * be used.
		 *
		 * v1.2 tables (some NV6/10, and NV15+) normally have the
		 * same 5 entries, which are not specific to the card and so
		 * no use.
		 *
		 * v1.2 does have an I2C table that read_dcb_i2c_table can
		 * handle, but cards exist (nv11 in #14821) with a bad i2c
		 * table pointer, so use the indices parsed in
		 * parse_bmp_structure.
		 *
		 * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
		 */
		NV_WARNONCE(dev, "No useful DCB data in VBIOS\n");
		return NULL;
	}

	NV_WARNONCE(dev, "DCB header validation failed\n");
	return NULL;
}

u8 *
dcb_outp(struct drm_device *dev, u8 idx)
{
	u8 *dcb = dcb_table(dev);
	if (dcb && dcb[0] >= 0x30) {
		if (idx < dcb[2])
			return dcb + dcb[1] + (idx * dcb[3]);
	} else
	if (dcb && dcb[0] >= 0x20) {
		u8 *i2c = ROMPTR(dev, dcb[2]);
		u8 *ent = dcb + 8 + (idx * 8);
		if (i2c && ent < i2c)
			return ent;
	} else
	if (dcb && dcb[0] >= 0x15) {
		u8 *i2c = ROMPTR(dev, dcb[2]);
		u8 *ent = dcb + 4 + (idx * 10);
		if (i2c && ent < i2c)
			return ent;
	}

	return NULL;
}

int
dcb_outp_foreach(struct drm_device *dev, void *data,
		 int (*exec)(struct drm_device *, void *, int idx, u8 *outp))
{
	int ret, idx = -1;
	u8 *outp = NULL;
	while ((outp = dcb_outp(dev, ++idx))) {
		if (ROM32(outp[0]) == 0x00000000)
			break; /* seen on an NV11 with DCB v1.5 */
		if (ROM32(outp[0]) == 0xffffffff)
			break; /* seen on an NV17 with DCB v2.0 */

		if ((outp[0] & 0x0f) == OUTPUT_UNUSED)
			continue;
		if ((outp[0] & 0x0f) == OUTPUT_EOL)
			break;

		ret = exec(dev, data, idx, outp);
		if (ret)
			return ret;
	}

	return 0;
}

static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb)
{
	struct dcb_entry *entry = &dcb->entry[dcb->entries];
@@ -6251,25 +6354,6 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb,
	return true;
}

static bool parse_dcb_entry(struct drm_device *dev, struct dcb_table *dcb,
			    uint32_t conn, uint32_t conf)
{
	struct dcb_entry *entry = new_dcb_entry(dcb);
	bool ret;

	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 ret;

	read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
			   entry->i2c_index, &dcb->i2c[entry->i2c_index]);

	return true;
}

static
void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb)
{
@@ -6446,88 +6530,62 @@ fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios)
}

static int
parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct dcb_table *dcb = &bios->dcb;
	uint16_t dcbptr = 0, i2ctabptr = 0;
	uint8_t *dcbtable;
	uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES;
	bool configblock = true;
	int recordlength = 8, confofs = 4;
	int i;
	struct dcb_table *dcb = &dev_priv->vbios.dcb;
	u32 conf = (dcb->version >= 0x20) ? ROM32(outp[4]) : ROM32(outp[6]);
	u32 conn = ROM32(outp[0]);
	bool ret;

	/* get the offset from 0x36 */
	if (dev_priv->card_type > NV_04) {
		dcbptr = ROM16(bios->data[0x36]);
		if (dcbptr == 0x0000)
			NV_WARN(dev, "No output data (DCB) found in BIOS\n");
	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);

		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 */

		read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
				   entry->i2c_index,
				   &dcb->i2c[entry->i2c_index]);
	}

	/* this situation likely means a really old card, pre DCB */
	if (dcbptr == 0x0) {
		fabricate_dcb_encoder_table(dev, bios);
	return 0;
}

	dcbtable = &bios->data[dcbptr];

	/* get DCB version */
	dcb->version = dcbtable[0];
	NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n",
		 dcb->version >> 4, dcb->version & 0xf);

	if (dcb->version >= 0x20) { /* NV17+ */
		uint32_t sig;
static int
parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
{
	struct dcb_table *dcb = &bios->dcb;
	u16 i2ctabptr = 0x0000;
	u8 *dcbt;

		if (dcb->version >= 0x30) { /* NV40+ */
			headerlen = dcbtable[1];
			entries = dcbtable[2];
			recordlength = dcbtable[3];
			i2ctabptr = ROM16(dcbtable[4]);
			sig = ROM32(dcbtable[6]);
			dcb->gpio_table_ptr = ROM16(dcbtable[10]);
			dcb->connector_table_ptr = ROM16(dcbtable[20]);
		} else {
			i2ctabptr = ROM16(dcbtable[2]);
			sig = ROM32(dcbtable[4]);
			headerlen = 8;
	dcbt = dcb_table(dev);
	if (!dcbt) {
		/* handle pre-DCB boards */
		if (bios->type == NVBIOS_BMP) {
			fabricate_dcb_encoder_table(dev, bios);
			return 0;
		}

		if (sig != 0x4edcbdcb) {
			NV_ERROR(dev, "Bad Display Configuration Block "
					"signature (%08X)\n", sig);
		return -EINVAL;
	}
	} else if (dcb->version >= 0x15) { /* some NV11 and NV20 */
		char sig[8] = { 0 };

		strncpy(sig, (char *)&dcbtable[-7], 7);
		i2ctabptr = ROM16(dcbtable[2]);
		recordlength = 10;
		confofs = 6;
	NV_TRACE(dev, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf);

		if (strcmp(sig, "DEV_REC")) {
			NV_ERROR(dev, "Bad Display Configuration Block "
					"signature (%s)\n", sig);
			return -EINVAL;
		}
	} else {
		/*
		 * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always
		 * has the same single (crt) entry, even when tv-out present, so
		 * the conclusion is this version cannot really be used.
		 * v1.2 tables (some NV6/10, and NV15+) normally have the same
		 * 5 entries, which are not specific to the card and so no use.
		 * v1.2 does have an I2C table that read_dcb_i2c_table can
		 * handle, but cards exist (nv11 in #14821) with a bad i2c table
		 * pointer, so use the indices parsed in parse_bmp_structure.
		 * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
		 */
		NV_TRACEWARN(dev, "No useful information in BIOS output table; "
				  "adding all possible outputs\n");
		fabricate_dcb_encoder_table(dev, bios);
		return 0;
	dcb->version = dcbt[0];
	if (dcb->version >= 0x30) {
		i2ctabptr = ROM16(dcbt[4]);
		dcb->gpio_table_ptr = ROM16(dcbt[10]);
		dcb->connector_table_ptr = ROM16(dcbt[20]);
	} else
	if (dcb->version >= 0x15) {
		i2ctabptr = ROM16(dcbt[2]);
	}

	if (!i2ctabptr)
@@ -6543,44 +6601,14 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
		 */
		if (dcb->version >= 0x22) {
			int idx = (dcb->version >= 0x40 ?
				   dcb->i2c_default_indices & 0xf :
				   2);
				   dcb->i2c_default_indices & 0xf : 2);

			read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
					   idx, &dcb->i2c[idx]);
		}
	}

	if (entries > DCB_MAX_NUM_ENTRIES)
		entries = DCB_MAX_NUM_ENTRIES;

	for (i = 0; i < entries; i++) {
		uint32_t connection, config = 0;

		connection = ROM32(dcbtable[headerlen + recordlength * i]);
		if (configblock)
			config = ROM32(dcbtable[headerlen + confofs + recordlength * i]);

		/* seen on an NV11 with DCB v1.5 */
		if (connection == 0x00000000)
			break;

		/* seen on an NV17 with DCB v2.0 */
		if (connection == 0xffffffff)
			break;

		if ((connection & 0x0000000f) == 0x0000000f)
			continue;

		if (!apply_dcb_encoder_quirks(dev, i, &connection, &config))
			continue;

		NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
			     dcb->entries, connection, config);

		if (!parse_dcb_entry(dev, dcb, connection, config))
			break;
	}
	dcb_outp_foreach(dev, NULL, parse_dcb_entry);

	/*
	 * apart for v2.1+ not being known for requiring merging, this
+6 −0
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ enum dcb_type {
	OUTPUT_LVDS = 3,
	OUTPUT_DP = 6,
	OUTPUT_EOL = 14, /* DCB 4.0+, appears to be end-of-list */
	OUTPUT_UNUSED = 15,
	OUTPUT_ANY = -1
};

@@ -339,4 +340,9 @@ struct nvbios {
	} legacy;
};

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));

#endif
+7 −0
Original line number Diff line number Diff line
@@ -1608,6 +1608,13 @@ extern void nv_wo32(struct nouveau_gpuobj *, u32 offset, u32 val);
#define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg)
#define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
#define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg)
#define NV_WARNONCE(d, fmt, arg...) do {                                       \
	static int _warned = 0;                                                \
	if (!_warned) {                                                        \
		NV_WARN(d, fmt, ##arg);                                        \
		_warned = 1;                                                   \
	}                                                                      \
} while(0)

/* nouveau_reg_debug bitmask */
enum {