ソースを参照

drm/nouveau/bios: implement some dcb output entry parsing/matching functions

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Ben Skeggs 12 年 前
コミット
75f8693f30

+ 5 - 25
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c

@@ -576,8 +576,7 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
 	    struct nvbios_outp *info)
 {
 	struct nouveau_bios *bios = nouveau_bios(priv);
-	u16 data, idx = 0;
-	u16 mask, type;
+	u16 mask, type, data;
 
 	if (outp < 4) {
 		type = DCB_OUTPUT_ANALOG;
@@ -602,30 +601,11 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
 	mask |= 0x0001 << outp;
 	mask |= 0x0100 << head;
 
-	/* this is a tad special, but for the moment its needed to get
-	 * all the dcb data required by the vbios scripts.. will be cleaned
-	 * up later as more bits are moved to the core..
-	 */
-	while ((data = dcb_outp(bios, idx++, ver, hdr))) {
-		u32 conn = nv_ro32(bios, data + 0);
-		u32 conf = nv_ro32(bios, data + 4);
-		if ((conn & 0x00300000) ||
-		    (conn & 0x0000000f) != type ||
-		    (conn & 0x0f000000) != (0x01000000 << outp))
-			continue;
-
-		if ( (mask & 0x00c0) && (mask & 0x00c0) !=
-		    ((mask & 0x00c0) & ((conf & 0x00000030) << 2)))
-			continue;
-
-		dcb->type = type;
-		dcb->or = 1 << outp;
-		dcb->connector = (conn & 0x0000f000) >> 12;
-
-		return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
-	}
+	data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
+	if (!data)
+		return 0x0000;
 
-	return 0x0000;
+	return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
 }
 
 static bool

+ 4 - 23
drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c

@@ -53,37 +53,18 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
 	const u8  link = (mthd & NV50_DISP_SOR_MTHD_LINK) >> 2;
 	const u8    or = (mthd & NV50_DISP_SOR_MTHD_OR);
 	const u16 mask = (0x0100 << head) | (0x0040 << link) | (0x0001 << or);
-	struct dcb_output outp = {
-		.type = type,
-		.or = (1 << or),
-		.sorconf.link = (1 << link),
-	};
-	u8  ver, hdr, idx = 0;
+	struct dcb_output outp;
+	u8  ver, hdr;
 	u32 data;
 	int ret = -EINVAL;
 
 	if (size < sizeof(u32))
 		return -EINVAL;
+	data = *(u32 *)args;
 
-	while (type && (data = dcb_outp(bios, idx++, &ver, &hdr))) {
-		u32 conn = nv_ro32(bios, data + 0);
-		u32 conf = nv_ro32(bios, data + 4);
-		if ((conn & 0x00300000) ||
-		    (conn & 0x0000000f) != type ||
-		    (conn & 0x0f000000) != (0x01000000 << or))
-			continue;
-
-		if ( (mask & 0x00c0) && (mask & 0x00c0) !=
-		    ((mask & 0x00c0) & ((conf & 0x00000030) << 2)))
-			continue;
-
-		outp.connector = (conn & 0x0000f000) >> 12;
-	}
-
-	if (data == 0x0000)
+	if (type && !dcb_outp_match(bios, type, mask, &ver, &hdr, &outp))
 		return -ENODEV;
 
-	data = *(u32 *)args;
 	switch (mthd & ~0x3f) {
 	case NV50_DISP_SOR_PWR:
 		ret = priv->sor.power(priv, or, data);

+ 5 - 0
drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h

@@ -23,6 +23,7 @@ struct dcb_output {
 	uint8_t bus;
 	uint8_t location;
 	uint8_t or;
+	uint8_t link;
 	bool duallink_possible;
 	union {
 		struct sor_conf {
@@ -55,6 +56,10 @@ struct dcb_output {
 
 u16 dcb_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *ent, u8 *len);
 u16 dcb_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len);
+u16 dcb_outp_parse(struct nouveau_bios *, u8 idx, u8 *, u8 *,
+		   struct dcb_output *);
+u16 dcb_outp_match(struct nouveau_bios *, u16 type, u16 mask, u8 *, u8 *,
+		   struct dcb_output *);
 int dcb_outp_foreach(struct nouveau_bios *, void *data, int (*exec)
 		     (struct nouveau_bios *, void *, int index, u16 entry));
 

+ 63 - 0
drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c

@@ -107,6 +107,69 @@ dcb_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
 	return 0x0000;
 }
 
+u16
+dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
+	       struct dcb_output *outp)
+{
+	u16 dcb = dcb_outp(bios, idx, ver, len);
+	if (dcb) {
+		if (*ver >= 0x20) {
+			u32 conn = nv_ro32(bios, dcb + 0x00);
+			outp->or        = (conn & 0x0f000000) >> 24;
+			outp->location  = (conn & 0x00300000) >> 20;
+			outp->bus       = (conn & 0x000f0000) >> 16;
+			outp->connector = (conn & 0x0000f000) >> 12;
+			outp->heads     = (conn & 0x00000f00) >> 8;
+			outp->i2c_index = (conn & 0x000000f0) >> 4;
+			outp->type      = (conn & 0x0000000f);
+			outp->link      = 0;
+		} else {
+			dcb = 0x0000;
+		}
+
+		if (*ver >= 0x40) {
+			u32 conf = nv_ro32(bios, dcb + 0x04);
+			switch (outp->type) {
+			case DCB_OUTPUT_TMDS:
+			case DCB_OUTPUT_LVDS:
+			case DCB_OUTPUT_DP:
+				outp->link = (conf & 0x00000030) >> 4;
+				outp->sorconf.link = outp->link; /*XXX*/
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	return dcb;
+}
+
+static inline u16
+dcb_outp_hasht(struct dcb_output *outp)
+{
+	return outp->type;
+}
+
+static inline u16
+dcb_outp_hashm(struct dcb_output *outp)
+{
+	return (outp->heads << 8) | (outp->link << 6) | outp->or;
+}
+
+u16
+dcb_outp_match(struct nouveau_bios *bios, u16 type, u16 mask,
+	       u8 *ver, u8 *len, struct dcb_output *outp)
+{
+	u16 dcb, idx = 0;
+	while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) {
+		if (dcb_outp_hasht(outp) == type) {
+			if ((dcb_outp_hashm(outp) & mask) == mask)
+				break;
+		}
+	}
+	return dcb;
+}
+
 int
 dcb_outp_foreach(struct nouveau_bios *bios, void *data,
 		 int (*exec)(struct nouveau_bios *, void *, int, u16))