diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_bios.c')
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_bios.c | 157 | 
1 files changed, 86 insertions, 71 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 71247da17da..abc382a9918 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -2573,48 +2573,34 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)  	 * each GPIO according to various values listed in each entry  	 */ -	const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; +	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;  	const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; -	const uint8_t *gpio_table = &bios->data[bios->dcb.gpio_table_ptr]; -	const uint8_t *gpio_entry;  	int i; -	if (!iexec->execute) -		return 1; - -	if (bios->dcb.version != 0x40) { -		NV_ERROR(bios->dev, "DCB table not version 4.0\n"); -		return 0; +	if (dev_priv->card_type != NV_50) { +		NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n"); +		return -ENODEV;  	} -	if (!bios->dcb.gpio_table_ptr) { -		NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n"); -		return 0; -	} +	if (!iexec->execute) +		return 1; -	gpio_entry = gpio_table + gpio_table[1]; -	for (i = 0; i < gpio_table[2]; i++, gpio_entry += gpio_table[3]) { -		uint32_t entry = ROM32(gpio_entry[0]), r, s, v; -		int line = (entry & 0x0000001f); +	for (i = 0; i < bios->dcb.gpio.entries; i++) { +		struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i]; +		uint32_t r, s, v; -		BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, entry); +		BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry); -		if ((entry & 0x0000ff00) == 0x0000ff00) -			continue; +		nv50_gpio_set(bios->dev, gpio->tag, gpio->state_default); -		r = nv50_gpio_reg[line >> 3]; -		s = (line & 0x07) << 2; -		v = bios_rd32(bios, r) & ~(0x00000003 << s); -		if (entry & 0x01000000) -			v |= (((entry & 0x60000000) >> 29) ^ 2) << s; -		else -			v |= (((entry & 0x18000000) >> 27) ^ 2) << s; -		bios_wr32(bios, r, v); - -		r = nv50_gpio_ctl[line >> 4]; -		s = (line & 0x0f); +		/* The NVIDIA binary driver doesn't appear to actually do +		 * any of this, my VBIOS does however. +		 */ +		/* Not a clue, needs de-magicing */ +		r = nv50_gpio_ctl[gpio->line >> 4]; +		s = (gpio->line & 0x0f);  		v = bios_rd32(bios, r) & ~(0x00010001 << s); -		switch ((entry & 0x06000000) >> 25) { +		switch ((gpio->entry & 0x06000000) >> 25) {  		case 1:  			v |= (0x00000001 << s);  			break; @@ -3198,7 +3184,6 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int  	struct nvbios *bios = &dev_priv->vbios;  	unsigned int outputset = (dcbent->or == 4) ? 1 : 0;  	uint16_t scriptptr = 0, clktable; -	uint8_t clktableptr = 0;  	/*  	 * For now we assume version 3.0 table - g80 support will need some @@ -3217,26 +3202,29 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int  		scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]);  		break;  	case LVDS_RESET: +		clktable = bios->fp.lvdsmanufacturerpointer + 15; +		if (dcbent->or == 4) +			clktable += 8; +  		if (dcbent->lvdsconf.use_straps_for_mode) {  			if (bios->fp.dual_link) -				clktableptr += 2; -			if (bios->fp.BITbit1) -				clktableptr++; +				clktable += 4; +			if (bios->fp.if_is_24bit) +				clktable += 2;  		} else {  			/* using EDID */ -			uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4]; -			int fallbackcmpval = (dcbent->or == 4) ? 4 : 1; +			int cmpval_24bit = (dcbent->or == 4) ? 4 : 1;  			if (bios->fp.dual_link) { -				clktableptr += 2; -				fallbackcmpval *= 2; +				clktable += 4; +				cmpval_24bit <<= 1;  			} -			if (fallbackcmpval & fallback) -				clktableptr++; + +			if (bios->fp.strapless_is_24bit & cmpval_24bit) +				clktable += 2;  		} -		/* adding outputset * 8 may not be correct */ -		clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]); +		clktable = ROM16(bios->data[clktable]);  		if (!clktable) {  			NV_ERROR(dev, "Pixel clock comparison table not found\n");  			return -ENOENT; @@ -3545,7 +3533,7 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b  	 * at which modes should be set up in the dual link style.  	 *  	 * Following the header, the BMP (ver 0xa) table has several records, -	 * indexed by a seperate xlat table, indexed in turn by the fp strap in +	 * indexed by a separate xlat table, indexed in turn by the fp strap in  	 * EXTDEV_BOOT. Each record had a config byte, followed by 6 script  	 * numbers for use by INIT_SUB which controlled panel init and power,  	 * and finally a dword of ms to sleep between power off and on @@ -3638,37 +3626,40 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b  		*if_is_24bit = bios->data[lvdsofs] & 16;  		break;  	case 0x30: -		/* -		 * My money would be on there being a 24 bit interface bit in -		 * this table, but I have no example of a laptop bios with a -		 * 24 bit panel to confirm that. Hence we shout loudly if any -		 * bit other than bit 0 is set (I've not even seen bit 1) -		 */ -		if (bios->data[lvdsofs] > 1) -			NV_ERROR(dev, -				 "You have a very unusual laptop display; please report it\n"); +	case 0x40:  		/*  		 * No sign of the "power off for reset" or "reset for panel  		 * on" bits, but it's safer to assume we should  		 */  		bios->fp.power_off_for_reset = true;  		bios->fp.reset_after_pclk_change = true; +  		/*  		 * It's ok lvdsofs is wrong for nv4x edid case; dual_link is -		 * over-written, and BITbit1 isn't used +		 * over-written, and if_is_24bit isn't used  		 */  		bios->fp.dual_link = bios->data[lvdsofs] & 1; -		bios->fp.BITbit1 = bios->data[lvdsofs] & 2; -		bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10; -		break; -	case 0x40: -		bios->fp.dual_link = bios->data[lvdsofs] & 1;  		bios->fp.if_is_24bit = bios->data[lvdsofs] & 2;  		bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4];  		bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;  		break;  	} +	/* Dell Latitude D620 reports a too-high value for the dual-link +	 * transition freq, causing us to program the panel incorrectly. +	 * +	 * It doesn't appear the VBIOS actually uses its transition freq +	 * (90000kHz), instead it uses the "Number of LVDS channels" field +	 * out of the panel ID structure (http://www.spwg.org/). +	 * +	 * For the moment, a quirk will do :) +	 */ +	if ((dev->pdev->device == 0x01d7) && +	    (dev->pdev->subsystem_vendor == 0x1028) && +	    (dev->pdev->subsystem_device == 0x01c2)) { +		bios->fp.duallink_transition_clk = 80000; +	} +  	/* set dual_link flag for EDID case */  	if (pxclk && (chip_version < 0x25 || chip_version > 0x28))  		bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk); @@ -5077,25 +5068,25 @@ parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset)  	gpio->tag = tag;  	gpio->line = line;  	gpio->invert = flags != 4; +	gpio->entry = ent;  }  static void  parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset)  { +	uint32_t entry = ROM32(bios->data[offset]);  	struct dcb_gpio_entry *gpio; -	uint32_t ent = ROM32(bios->data[offset]); -	uint8_t line = ent & 0x1f, -		tag = ent >> 8 & 0xff; -	if (tag == 0xff) +	if ((entry & 0x0000ff00) == 0x0000ff00)  		return;  	gpio = new_gpio_entry(bios); - -	/* Currently unused, we may need more fields parsed at some -	 * point. */ -	gpio->tag = tag; -	gpio->line = line; +	gpio->tag = (entry & 0x0000ff00) >> 8; +	gpio->line = (entry & 0x0000001f) >> 0; +	gpio->state_default = (entry & 0x01000000) >> 24; +	gpio->state[0] = (entry & 0x18000000) >> 27; +	gpio->state[1] = (entry & 0x60000000) >> 29; +	gpio->entry = entry;  }  static void @@ -5211,6 +5202,21 @@ divine_connector_type(struct nvbios *bios, int index)  }  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 ((dev->pdev->device == 0x0421) && +	    (dev->pdev->subsystem_vendor == 0x1458) && +	    (dev->pdev->subsystem_device == 0x344c)) { +		if (cte->type == DCB_CONNECTOR_HDMI_1) +			cte->type = DCB_CONNECTOR_DVI_I; +	} +} + +static void  parse_dcb_connector_table(struct nvbios *bios)  {  	struct drm_device *dev = bios->dev; @@ -5238,13 +5244,14 @@ parse_dcb_connector_table(struct nvbios *bios)  	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->index = (cte->entry & 0x00000f00) >> 8; +		cte->index2 = (cte->entry & 0x00000f00) >> 8;  		switch (cte->entry & 0x00033000) {  		case 0x00001000:  			cte->gpio_tag = 0x07; @@ -5266,6 +5273,8 @@ parse_dcb_connector_table(struct nvbios *bios)  		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); @@ -5287,10 +5296,16 @@ parse_dcb_connector_table(struct nvbios *bios)  			break;  		default:  			cte->type = divine_connector_type(bios, cte->index); -			NV_WARN(dev, "unknown type, using 0x%02x", cte->type); +			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); +		} +  	}  }  |