diff options
36 files changed, 5503 insertions, 19 deletions
| diff --git a/linux-core/Makefile b/linux-core/Makefile index b9405bbb..9b288851 100644 --- a/linux-core/Makefile +++ b/linux-core/Makefile @@ -90,7 +90,7 @@ VIAHEADERS =	via_drm.h via_drv.h via_3d_reg.h via_verifier.h $(DRMHEADERS)  MACH64HEADERS = mach64_drv.h mach64_drm.h $(DRMHEADERS)  NVHEADERS =     nv_drv.h $(DRMHEADERS)  FFBHEADERS = 	ffb_drv.h $(DRMHEADERS) -NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h $(DRMHEADERS) +NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h nouveau_display.h $(DRMHEADERS)  XGIHEADERS = xgi_cmdlist.h xgi_drv.h xgi_misc.h xgi_regs.h $(DRMHEADERS)  RADEONMSHEADERS = radeon_ms_driver.h $(DRMHEADERS)  diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index ac9baf02..23430140 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -35,7 +35,10 @@ nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \  		nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \  		nv04_graph.o nv10_graph.o nv20_graph.o \  		nv40_graph.o nv50_graph.o \ -		nv04_instmem.o nv50_instmem.o +		nv04_instmem.o nv50_instmem.o \ +		nouveau_bios.o \ +		nv50_crtc.o nv50_cursor.o nv50_lut.o nv50_fb.o nv50_output.o nv50_sor.o nv50_dac.o nv50_connector.o nv50_i2c.o nv50_display.o \ +		nv50_kms_wrapper.o  radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o  radeon_ms-objs := radeon_ms_drv.o radeon_ms_drm.o radeon_ms_family.o \  		radeon_ms_state.o radeon_ms_bo.o radeon_ms_irq.o \ diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index e1b371cc..c8cfaef4 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -100,6 +100,7 @@ char *drm_get_connector_name(struct drm_connector *connector)  		 connector->connector_type_id);  	return buf;  } +EXPORT_SYMBOL(drm_get_connector_name);  char *drm_get_connector_status_name(enum drm_connector_status status)  { diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 2b577b93..d6fa4cca 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -619,6 +619,7 @@ extern void drm_fb_release(struct file *filp);  extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);  extern struct edid *drm_get_edid(struct drm_connector *connector,  				 struct i2c_adapter *adapter); +extern unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter);  extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);  extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);  extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index 0d600396..812e64a6 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -122,19 +122,30 @@ static bool edid_is_valid(struct edid *edid)  	if (memcmp(edid->header, edid_header, sizeof(edid_header)))  		goto bad; -	if (edid->version != 1) +	if (edid->version != 1) { +		DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);  		goto bad; -	if (edid->revision <= 0 || edid->revision > 3) +	} +	if (edid->revision <= 0 || edid->revision > 3) { +		DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision);  		goto bad; +	}  	for (i = 0; i < EDID_LENGTH; i++)  		csum += raw_edid[i]; -	if (csum) +	if (csum) { +		DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);  		goto bad; +	}  	return 1;  bad: +	if (raw_edid) { +		DRM_ERROR("Raw EDID:\n"); +		print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); +		printk("\n"); +	}  	return 0;  } @@ -545,7 +556,7 @@ static int add_detailed_info(struct drm_connector *connector,  #define DDC_ADDR 0x50 -static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter) +unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)  {  	unsigned char start = 0x0;  	unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); @@ -576,6 +587,7 @@ static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)  	kfree(buf);  	return NULL;  } +EXPORT_SYMBOL(drm_do_probe_ddc_edid);  static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)  { @@ -589,15 +601,15 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)  	 *   Then set clock & data low  	 */  	algo_data->setscl(algo_data->data, 1); -	udelay(550); /* startup delay */ -	algo_data->setscl(algo_data->data, 0); -	algo_data->setsda(algo_data->data, 0); +	//udelay(550); /* startup delay */ +	//algo_data->setscl(algo_data->data, 0); +	//algo_data->setsda(algo_data->data, 0);  	for (i = 0; i < 3; i++) {  		/* For some old monitors we need the  		 * following process to initialize/stop DDC  		 */ -		algo_data->setsda(algo_data->data, 0); +		algo_data->setsda(algo_data->data, 1);  		msleep(13);  		algo_data->setscl(algo_data->data, 1); @@ -632,16 +644,16 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)  		algo_data->setsda(algo_data->data, 1);  		msleep(15);  		algo_data->setscl(algo_data->data, 0); +		algo_data->setsda(algo_data->data, 0);  		if (edid)  			break;  	}  	/* Release the DDC lines when done or the Apple Cinema HD display  	 * will switch off  	 */ -	algo_data->setsda(algo_data->data, 0); -	algo_data->setscl(algo_data->data, 0); +	algo_data->setsda(algo_data->data, 1);  	algo_data->setscl(algo_data->data, 1); - +	  	return edid;  } diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index 3ed427fb..df670c74 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -89,6 +89,7 @@ void drm_mode_list_concat(struct list_head *head, struct list_head *new)  		list_move_tail(entry, new);  	}  } +EXPORT_SYMBOL(drm_mode_list_concat);  /**   * drm_mode_width - get the width of a mode @@ -401,6 +402,7 @@ void drm_mode_prune_invalid(struct drm_device *dev,  		}  	}  } +EXPORT_SYMBOL(drm_mode_prune_invalid);  /**   * drm_mode_compare - compare modes for favorability @@ -525,7 +527,7 @@ void drm_mode_sort(struct list_head *mode_list)  {  	list_sort(mode_list, drm_mode_compare);  } - +EXPORT_SYMBOL(drm_mode_sort);  /**   * drm_mode_connector_list_update - update the mode list for the connector @@ -564,3 +566,4 @@ void drm_mode_connector_list_update(struct drm_connector *connector)  		}  	}  } +EXPORT_SYMBOL(drm_mode_connector_list_update); diff --git a/linux-core/nouveau_bios.c b/linux-core/nouveau_bios.c new file mode 100644 index 00000000..83647418 --- /dev/null +++ b/linux-core/nouveau_bios.c @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2005-2006 Erik Waling + * Copyright (C) 2006 Stephane Marchesin + * Copyright (C) 2007-2008 Stuart Bennett + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <asm/byteorder.h> +#include "nouveau_bios.h" +#include "nouveau_drv.h" + +/* returns true if it mismatches */ +static bool nv_checksum(const uint8_t *data, unsigned int length) +{ +	/* there's a few checksums in the BIOS, so here's a generic checking function */ +	int i; +	uint8_t sum = 0; + +	for (i = 0; i < length; i++) +		sum += data[i]; + +	if (sum) +		return true; + +	return false; +} + +static int nv_valid_bios(struct drm_device *dev, uint8_t *data) +{ +	/* check for BIOS signature */ +	if (!(data[0] == 0x55 && data[1] == 0xAA)) { +		DRM_ERROR("BIOS signature not found.\n"); +		return 0; +	} + +	if (nv_checksum(data, data[2] * 512)) { +		DRM_ERROR("BIOS checksum invalid.\n"); +		return 1; +	} + +	return 2; +} + +static void nv_shadow_bios_rom(struct drm_device *dev, uint8_t *data) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	int i; + +	/* enable access to rom */ +	NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED); + +	/* This is also valid for pre-NV50, it just happened to be the only define already present. */ +	for (i=0; i < NV50_PROM__ESIZE; i++) { +		/* Appearantly needed for a 6600GT/6800LE bug. */ +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +	} + +	/* disable access to rom */ +	NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); +} + +static void nv_shadow_bios_ramin(struct drm_device *dev, uint8_t *data) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	uint32_t old_bar0_pramin = 0; +	int i; + +	/* Move the bios copy to the start of ramin? */ +	if (dev_priv->card_type >= NV_50) { +		uint32_t vbios_vram = (NV_READ(0x619f04) & ~0xff) << 8; + +		if (!vbios_vram) +			vbios_vram = (NV_READ(0x1700) << 16) + 0xf0000; + +		old_bar0_pramin = NV_READ(0x1700); +		NV_WRITE(0x1700, vbios_vram >> 16); +	} + +	for (i=0; i < NV50_PROM__ESIZE; i++) +		data[i] = DRM_READ8(dev_priv->mmio, NV04_PRAMIN + i); + +	if (dev_priv->card_type >= NV_50) +		NV_WRITE(0x1700, old_bar0_pramin); +} + +static bool nv_shadow_bios(struct drm_device *dev, uint8_t *data) +{ +	nv_shadow_bios_rom(dev, data); +	if (nv_valid_bios(dev, data) == 2) +		return true; + +	nv_shadow_bios_ramin(dev, data); +	if (nv_valid_bios(dev, data)) +		return true; + +	return false; +} + +struct bit_entry { +	uint8_t id[2]; +	uint16_t length; +	uint16_t offset; +}; + +static int parse_bit_C_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry) +{ +	/* offset + 8  (16 bits): PLL limits table pointer +	 * +	 * There's more in here, but that's unknown. +	 */ + +	if (bitentry->length < 10) { +		DRM_ERROR( "Do not understand BIT C table\n"); +		return 0; +	} + +	bios->pll_limit_tbl_ptr = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset + 8]))); + +	return 1; +} + +static void parse_bit_structure(struct drm_device *dev, struct bios *bios, const uint16_t bitoffset) +{ +	int entries = bios->data[bitoffset + 4]; +	/* parse i first, I next (which needs C & M before it), and L before D */ +	char parseorder[] = "iCMILDT"; +	struct bit_entry bitentry; +	int i, j, offset; + +	for (i = 0; i < sizeof(parseorder); i++) { +		for (j = 0, offset = bitoffset + 6; j < entries; j++, offset += 6) { +			bitentry.id[0] = bios->data[offset]; +			bitentry.id[1] = bios->data[offset + 1]; +			bitentry.length = le16_to_cpu(*((uint16_t *)&bios->data[offset + 2])); +			bitentry.offset = le16_to_cpu(*((uint16_t *)&bios->data[offset + 4])); + +			if (bitentry.id[0] != parseorder[i]) +				continue; + +			switch (bitentry.id[0]) { +			case 'C': +				parse_bit_C_tbl_entry(dev, bios, &bitentry); +				break; +			} +		} +	} +} + +static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) +{ +	int i, j; + +	for (i = 0; i <= (n - len); i++) { +		for (j = 0; j < len; j++) +			if (data[i + j] != str[j]) +				break; +		if (j == len) +			return i; +	} + +	return 0; +} + +static void +read_dcb_i2c_entry(struct drm_device *dev, uint8_t dcb_version, uint16_t i2ctabptr, int index) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct bios *bios = &dev_priv->bios; +	uint8_t *i2ctable = &bios->data[i2ctabptr]; +	uint8_t headerlen = 0; +	int i2c_entries = MAX_NUM_DCB_ENTRIES; +	int recordoffset = 0, rdofs = 1, wrofs = 0; + +	if (!i2ctabptr) +		return; + +	if (dcb_version >= 0x30) { +		if (i2ctable[0] != dcb_version) /* necessary? */ +			DRM_ERROR( +				   "DCB I2C table version mismatch (%02X vs %02X)\n", +				   i2ctable[0], dcb_version); +		headerlen = i2ctable[1]; +		i2c_entries = i2ctable[2]; + +		/* same address offset used for read and write for C51 and G80 */ +		if (bios->chip_version == 0x51) +			rdofs = wrofs = 1; +		if (i2ctable[0] >= 0x40) +			rdofs = wrofs = 0; +	} +	/* it's your own fault if you call this function on a DCB 1.1 BIOS -- +	 * the test below is for DCB 1.2 +	 */ +	if (dcb_version < 0x14) { +		recordoffset = 2; +		rdofs = 0; +		wrofs = 1; +	} + +	if (index == 0xf) +		return; +	if (index > i2c_entries) { +		DRM_ERROR( +			   "DCB I2C index too big (%d > %d)\n", +			   index, i2ctable[2]); +		return; +	} +	if (i2ctable[headerlen + 4 * index + 3] == 0xff) { +		DRM_ERROR( +			   "DCB I2C entry invalid\n"); +		return; +	} + +	if (bios->chip_version == 0x51) { +		int port_type = i2ctable[headerlen + 4 * index + 3]; + +		if (port_type != 4) +			DRM_ERROR( +				   "DCB I2C table has port type %d\n", port_type); +	} +	if (i2ctable[0] >= 0x40) { +		int port_type = i2ctable[headerlen + 4 * index + 3]; + +		if (port_type != 5) +			DRM_ERROR( +				   "DCB I2C table has port type %d\n", port_type); +	} + +	dev_priv->dcb_table.i2c_read[index] = i2ctable[headerlen + recordoffset + rdofs + 4 * index]; +	dev_priv->dcb_table.i2c_write[index] = i2ctable[headerlen + recordoffset + wrofs + 4 * index]; +} + +static bool +parse_dcb_entry(struct drm_device *dev, int index, uint8_t dcb_version, uint16_t i2ctabptr, uint32_t conn, uint32_t conf) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct dcb_entry *entry = &dev_priv->dcb_table.entry[index]; + +	memset(entry, 0, sizeof (struct dcb_entry)); + +	entry->index = index; +	/* safe defaults for a crt */ +	entry->type = 0; +	entry->i2c_index = 0; +	entry->heads = 1; +	entry->bus = 0; +	entry->location = LOC_ON_CHIP; +	entry->or = 1; +	entry->duallink_possible = false; + +	if (dcb_version >= 0x20) { +		entry->type = conn & 0xf; +		entry->i2c_index = (conn >> 4) & 0xf; +		entry->heads = (conn >> 8) & 0xf; +		entry->bus = (conn >> 16) & 0xf; +		entry->location = (conn >> 20) & 0xf; +		entry->or = (conn >> 24) & 0xf; +		/* Normal entries consist of a single bit, but dual link has the +		 * adjacent more significant bit set too +		 */ +		if ((1 << (ffs(entry->or) - 1)) * 3 == entry->or) +			entry->duallink_possible = true; + +		switch (entry->type) { +		case DCB_OUTPUT_LVDS: +			{ +			uint32_t mask; +			if (conf & 0x1) +				entry->lvdsconf.use_straps_for_mode = true; +			if (dcb_version < 0x22) { +				mask = ~0xd; +				/* both 0x4 and 0x8 show up in v2.0 tables; assume they mean +				 * the same thing, which is probably wrong, but might work */ +				if (conf & 0x4 || conf & 0x8) +					entry->lvdsconf.use_power_scripts = true; +			} else { +				mask = ~0x5; +				if (conf & 0x4) +					entry->lvdsconf.use_power_scripts = true; +			} +			if (conf & mask) { +				DRM_ERROR( +					   "Unknown LVDS configuration bits, please report\n"); +				/* cause output setting to fail, so message is seen */ +				dev_priv->dcb_table.entries = 0; +				return false; +			} +			break; +			} +		case 0xe: +			/* weird type that appears on g80 mobile bios; nv driver treats it as a terminator */ +			return false; +		} +		read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); +	} else if (dcb_version >= 0x14 ) { +		if (conn != 0xf0003f00 && conn != 0xf2247f10 && conn != 0xf2204001 && conn != 0xf2204301 && conn != 0xf2244311 && conn != 0xf2045f14 && conn != 0xf2205004 && conn != 0xf2208001 && conn != 0xf4204011 && conn != 0xf4208011 && conn != 0xf4248011) { +			DRM_ERROR( +				   "Unknown DCB 1.4 / 1.5 entry, please report\n"); +			/* cause output setting to fail, so message is seen */ +			dev_priv->dcb_table.entries = 0; +			return false; +		} +		/* most of the below is a "best guess" atm */ +		entry->type = conn & 0xf; +		if (entry->type == 4) { /* digital */ +			if (conn & 0x10) +				entry->type = DCB_OUTPUT_LVDS; +			else +				entry->type = DCB_OUTPUT_TMDS; +		} +		/* what's in bits 5-13? could be some brooktree/chrontel/philips thing, in tv case */ +		entry->i2c_index = (conn >> 14) & 0xf; +		/* raw heads field is in range 0-1, so move to 1-2 */ +		entry->heads = ((conn >> 18) & 0x7) + 1; +		entry->location = (conn >> 21) & 0xf; +		entry->bus = (conn >> 25) & 0x7; +		/* set or to be same as heads -- hopefully safe enough */ +		entry->or = entry->heads; + +		switch (entry->type) { +		case DCB_OUTPUT_LVDS: +			/* this is probably buried in conn's unknown bits */ +			entry->lvdsconf.use_power_scripts = true; +			break; +		case DCB_OUTPUT_TMDS: +			/* invent a DVI-A output, by copying the fields of the DVI-D output +			 * reported to work by math_b on an NV20(!) */ +			memcpy(&entry[1], &entry[0], sizeof(struct dcb_entry)); +			entry[1].type = DCB_OUTPUT_ANALOG; +			dev_priv->dcb_table.entries++; +		} +		read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); +	} else if (dcb_version >= 0x12) { +		/* v1.2 tables normally have the same 5 entries, which are not +		 * specific to the card, so use the defaults for a crt */ +		/* DCB v1.2 does have an I2C table that read_dcb_i2c_table can handle, but cards +		 * exist (seen on nv11) where the pointer to the table points to the wrong +		 * place, so for now, we rely on the indices parsed in parse_bmp_structure +		 */ +		entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; +	} else { /* pre DCB / v1.1 - use the safe defaults for a crt */ +		DRM_ERROR( +			   "No information in BIOS output table; assuming a CRT output exists\n"); +		entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; +	} + +	if (entry->type == DCB_OUTPUT_LVDS && dev_priv->bios.fp.strapping != 0xff) +		entry->lvdsconf.use_straps_for_mode = true; + +	dev_priv->dcb_table.entries++; + +	return true; +} + +static void merge_like_dcb_entries(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	/* DCB v2.0 lists each output combination separately. +	 * Here we merge compatible entries to have fewer outputs, with more options +	 */ +	int i, newentries = 0; + +	for (i = 0; i < dev_priv->dcb_table.entries; i++) { +		struct dcb_entry *ient = &dev_priv->dcb_table.entry[i]; +		int j; + +		for (j = i + 1; j < dev_priv->dcb_table.entries; j++) { +			struct dcb_entry *jent = &dev_priv->dcb_table.entry[j]; + +			if (jent->type == 100) /* already merged entry */ +				continue; + +			/* merge heads field when all other fields the same */ +			if (jent->i2c_index == ient->i2c_index && jent->type == ient->type && jent->location == ient->location && jent->or == ient->or) { +				DRM_INFO( +					   "Merging DCB entries %d and %d\n", i, j); +				ient->heads |= jent->heads; +				jent->type = 100; /* dummy value */ +			} +		} +	} + +	/* Compact entries merged into others out of dcb_table */ +	for (i = 0; i < dev_priv->dcb_table.entries; i++) { +		if ( dev_priv->dcb_table.entry[i].type == 100 ) +			continue; + +		if (newentries != i) +			memcpy(&dev_priv->dcb_table.entry[newentries], &dev_priv->dcb_table.entry[i], sizeof(struct dcb_entry)); +		newentries++; +	} + +	dev_priv->dcb_table.entries = newentries; +} + +static unsigned int parse_dcb_table(struct drm_device *dev, struct bios *bios) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	uint16_t dcbptr, i2ctabptr = 0; +	uint8_t *dcbtable; +	uint8_t dcb_version, headerlen = 0x4, entries = MAX_NUM_DCB_ENTRIES; +	bool configblock = true; +	int recordlength = 8, confofs = 4; +	int i; + +	dev_priv->dcb_table.entries = 0; + +	/* get the offset from 0x36 */ +	dcbptr = le16_to_cpu(*(uint16_t *)&bios->data[0x36]); + +	if (dcbptr == 0x0) { +		DRM_ERROR( +			   "No Display Configuration Block pointer found\n"); +		/* this situation likely means a really old card, pre DCB, so we'll add the safe CRT entry */ +		parse_dcb_entry(dev, 0, 0, 0, 0, 0); +		return 1; +	} + +	dcbtable = &bios->data[dcbptr]; + +	/* get DCB version */ +	dcb_version = dcbtable[0]; +	DRM_INFO( +		   "Display Configuration Block version %d.%d found\n", +		   dcb_version >> 4, dcb_version & 0xf); + +	if (dcb_version >= 0x20) { /* NV17+ */ +		uint32_t sig; + +		if (dcb_version >= 0x30) { /* NV40+ */ +			headerlen = dcbtable[1]; +			entries = dcbtable[2]; +			recordlength = dcbtable[3]; +			i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[4]); +			sig = le32_to_cpu(*(uint32_t *)&dcbtable[6]); + +			DRM_INFO( +				   "DCB header length %d, with %d possible entries\n", +				   headerlen, entries); +		} else { +			i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); +			sig = le32_to_cpu(*(uint32_t *)&dcbtable[4]); +			headerlen = 8; +		} + +		if (sig != 0x4edcbdcb) { +			DRM_ERROR( +				   "Bad Display Configuration Block signature (%08X)\n", sig); +			return 0; +		} +	} else if (dcb_version >= 0x14) { /* some NV15/16, and NV11+ */ +		char sig[8]; + +		memset(sig, 0, 8); +		strncpy(sig, (char *)&dcbtable[-7], 7); +		i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); +		recordlength = 10; +		confofs = 6; + +		if (strcmp(sig, "DEV_REC")) { +			DRM_ERROR( +				   "Bad Display Configuration Block signature (%s)\n", sig); +			return 0; +		} +	} else if (dcb_version >= 0x12) { /* some NV6/10, and NV15+ */ +		i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); +		configblock = false; +	} else {	/* NV5+, maybe NV4 */ +		/* DCB 1.1 seems to be quite unhelpful - we'll just add the safe CRT entry */ +		parse_dcb_entry(dev, 0, dcb_version, 0, 0, 0); +		return 1; +	} + +	if (entries >= MAX_NUM_DCB_ENTRIES) +		entries = MAX_NUM_DCB_ENTRIES; + +	for (i = 0; i < entries; i++) { +		uint32_t connection, config = 0; + +		connection = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + recordlength * i]); +		if (configblock) +			config = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + confofs + recordlength * i]); + +		/* Should we allow discontinuous DCBs? Certainly DCB I2C tables can be discontinuous */ +		if ((connection & 0x0000000f) == 0x0000000f) /* end of records */ +			break; +		if (connection == 0x00000000) /* seen on an NV11 with DCB v1.5 */ +			break; + +		DRM_INFO("Raw DCB entry %d: %08x %08x\n", i, connection, config); +		if (!parse_dcb_entry(dev, dev_priv->dcb_table.entries, dcb_version, i2ctabptr, connection, config)) +			break; +	} + +	merge_like_dcb_entries(dev); + +	return dev_priv->dcb_table.entries; +} + +int nouveau_parse_bios(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	const uint8_t bit_signature[] = { 'B', 'I', 'T' }; +	int offset; + +	dev_priv->bios.data = kzalloc(NV50_PROM__ESIZE, GFP_KERNEL); + +	if (!nv_shadow_bios(dev, dev_priv->bios.data)) +		return -EINVAL; + +	dev_priv->bios.length = dev_priv->bios.data[2] * 512; +	if (dev_priv->bios.length > NV50_PROM__ESIZE) +		dev_priv->bios.length = NV50_PROM__ESIZE; + +	if ((offset = findstr(dev_priv->bios.data, dev_priv->bios.length, bit_signature, sizeof(bit_signature)))) { +		DRM_INFO("BIT BIOS found\n"); +		parse_bit_structure(dev, &dev_priv->bios, offset + 4); +	} else { +		DRM_ERROR("BIT BIOS not found\n"); +		return -EINVAL; +	} + +	if (parse_dcb_table(dev, &dev_priv->bios)) +		DRM_INFO("Found %d entries in DCB\n", dev_priv->dcb_table.entries); + +	return 0; +} + +/* temporary */ +#define NV_RAMDAC_NVPLL			0x00680500 +#define NV_RAMDAC_MPLL			0x00680504 +#define NV_RAMDAC_VPLL			0x00680508 +#	define NV_RAMDAC_PLL_COEFF_MDIV			0x000000FF +#	define NV_RAMDAC_PLL_COEFF_NDIV			0x0000FF00 +#	define NV_RAMDAC_PLL_COEFF_PDIV			0x00070000 +#	define NV30_RAMDAC_ENABLE_VCO2			(1 << 7) +#define NV_RAMDAC_VPLL2			0x00680520 + +bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim) +{ +	/* PLL limits table +	 * +	 * Version 0x10: NV31 +	 * One byte header (version), one record of 24 bytes +	 * Version 0x11: NV36 - Not implemented +	 * Seems to have same record style as 0x10, but 3 records rather than 1 +	 * Version 0x20: Found on Geforce 6 cards +	 * Trivial 4 byte BIT header. 31 (0x1f) byte record length +	 * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards +	 * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record +	 * length in general, some (integrated) have an extra configuration byte +	 */ + +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct bios *bios = &dev_priv->bios; +	uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0; +	int pllindex = 0; +	uint32_t crystal_strap_mask, crystal_straps; + +	if (!bios->pll_limit_tbl_ptr) { +		if (bios->chip_version >= 0x40 || bios->chip_version == 0x31 || bios->chip_version == 0x36) { +			DRM_ERROR("Pointer to PLL limits table invalid\n"); +			return false; +		} +	} else { +		pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr]; + +		DRM_INFO("Found PLL limits table version 0x%X\n", pll_lim_ver); +	} + +	crystal_strap_mask = 1 << 6; +	/* open coded pNv->twoHeads test */ +	if (bios->chip_version > 0x10 && bios->chip_version != 0x15 && +		bios->chip_version != 0x1a && bios->chip_version != 0x20) +		crystal_strap_mask |= 1 << 22; +	crystal_straps = NV_READ(NV50_PEXTDEV + 0x0) & crystal_strap_mask; + +	switch (pll_lim_ver) { +	/* we use version 0 to indicate a pre limit table bios (single stage pll) +	 * and load the hard coded limits instead */ +	case 0: +		break; +	case 0x10: +	case 0x11: /* strictly v0x11 has 3 entries, but the last two don't seem to get used */ +		headerlen = 1; +		recordlen = 0x18; +		entries = 1; +		pllindex = 0; +		break; +	case 0x20: +	case 0x21: +		headerlen = bios->data[bios->pll_limit_tbl_ptr + 1]; +		recordlen = bios->data[bios->pll_limit_tbl_ptr + 2]; +		entries = bios->data[bios->pll_limit_tbl_ptr + 3]; +		break; +	default: +		DRM_ERROR("PLL limits table revision not currently supported\n"); +		return false; +	} + +	/* initialize all members to zero */ +	memset(pll_lim, 0, sizeof(struct pll_lims)); + +	if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) { +		uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex; + +		pll_lim->vco1.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs]))); +		pll_lim->vco1.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 4]))); +		pll_lim->vco2.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 8]))); +		pll_lim->vco2.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 12]))); +		pll_lim->vco1.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 16]))); +		pll_lim->vco2.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 20]))); +		pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX; + +		/* these values taken from nv30/31/36 */ +		pll_lim->vco1.min_n = 0x1; +		if (bios->chip_version == 0x36) +			pll_lim->vco1.min_n = 0x5; +		pll_lim->vco1.max_n = 0xff; +		pll_lim->vco1.min_m = 0x1; +		pll_lim->vco1.max_m = 0xd; +		pll_lim->vco2.min_n = 0x4; +		/* on nv30, 31, 36 (i.e. all cards with two stage PLLs with this +		 * table version (apart from nv35)), N2 is compared to +		 * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and +		 * save a comparison +		 */ +		pll_lim->vco2.max_n = 0x28; +		if (bios->chip_version == 0x30 || bios->chip_version == 0x35) +		       /* only 5 bits available for N2 on nv30/35 */ +			pll_lim->vco2.max_n = 0x1f; +		pll_lim->vco2.min_m = 0x1; +		pll_lim->vco2.max_m = 0x4; +	} else if (pll_lim_ver) {	/* ver 0x20, 0x21 */ +		uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen; +		uint32_t reg = 0; /* default match */ +		int i; + +		/* first entry is default match, if nothing better. warn if reg field nonzero */ +		if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs]))) +			DRM_ERROR("Default PLL limit entry has non-zero register field\n"); + +		if (limit_match > MAX_PLL_TYPES) +			/* we've been passed a reg as the match */ +			reg = limit_match; +		else /* limit match is a pll type */ +			for (i = 1; i < entries && !reg; i++) { +				uint32_t cmpreg = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + recordlen * i]))); + +				if (limit_match == NVPLL && (cmpreg == NV_RAMDAC_NVPLL || cmpreg == 0x4000)) +					reg = cmpreg; +				if (limit_match == MPLL && (cmpreg == NV_RAMDAC_MPLL || cmpreg == 0x4020)) +					reg = cmpreg; +				if (limit_match == VPLL1 && (cmpreg == NV_RAMDAC_VPLL || cmpreg == 0x4010)) +					reg = cmpreg; +				if (limit_match == VPLL2 && (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018)) +					reg = cmpreg; +			} + +		for (i = 1; i < entries; i++) +			if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs + recordlen * i])) == reg) { +				pllindex = i; +				break; +			} + +		plloffs += recordlen * pllindex; + +		DRM_INFO("Loading PLL limits for reg 0x%08x\n", pllindex ? reg : 0); + +		/* frequencies are stored in tables in MHz, kHz are more useful, so we convert */ + +		/* What output frequencies can each VCO generate? */ +		pll_lim->vco1.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 4]))) * 1000; +		pll_lim->vco1.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 6]))) * 1000; +		pll_lim->vco2.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 8]))) * 1000; +		pll_lim->vco2.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 10]))) * 1000; + +		/* What input frequencies do they accept (past the m-divider)? */ +		pll_lim->vco1.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 12]))) * 1000; +		pll_lim->vco2.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 14]))) * 1000; +		pll_lim->vco1.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 16]))) * 1000; +		pll_lim->vco2.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 18]))) * 1000; + +		/* What values are accepted as multiplier and divider? */ +		pll_lim->vco1.min_n = bios->data[plloffs + 20]; +		pll_lim->vco1.max_n = bios->data[plloffs + 21]; +		pll_lim->vco1.min_m = bios->data[plloffs + 22]; +		pll_lim->vco1.max_m = bios->data[plloffs + 23]; +		pll_lim->vco2.min_n = bios->data[plloffs + 24]; +		pll_lim->vco2.max_n = bios->data[plloffs + 25]; +		pll_lim->vco2.min_m = bios->data[plloffs + 26]; +		pll_lim->vco2.max_m = bios->data[plloffs + 27]; + +		pll_lim->unk1c = bios->data[plloffs + 28]; +		pll_lim->max_log2p_bias = bios->data[plloffs + 29]; +		pll_lim->log2p_bias = bios->data[plloffs + 30]; + +		if (recordlen > 0x22) +			pll_lim->refclk = le32_to_cpu(*((uint32_t *)&bios->data[plloffs + 31])); + +		if (recordlen > 0x23) +			if (bios->data[plloffs + 35]) +				DRM_ERROR("Bits set in PLL configuration byte (%x)\n", bios->data[plloffs + 35]); + +		/* C51 special not seen elsewhere */ +		/*if (bios->chip_version == 0x51 && !pll_lim->refclk) { +			uint32_t sel_clk = nv32_rd(pScrn, NV_RAMDAC_SEL_CLK); + +			if (((limit_match == NV_RAMDAC_VPLL || limit_match == VPLL1) && sel_clk & 0x20) || +			    ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) { +				if (nv_idx_port_rd(pScrn, CRTC_INDEX_COLOR, NV_VGA_CRTCX_REVISION) < 0xa3) +					pll_lim->refclk = 200000; +				else +					pll_lim->refclk = 25000; +			} +		}*/ +	} + +	/* By now any valid limit table ought to have set a max frequency for +	 * vco1, so if it's zero it's either a pre limit table bios, or one +	 * with an empty limit table (seen on nv18) +	 */ +	if (!pll_lim->vco1.maxfreq) { +		pll_lim->vco1.minfreq = bios->fminvco; +		pll_lim->vco1.maxfreq = bios->fmaxvco; +		pll_lim->vco1.min_inputfreq = 0; +		pll_lim->vco1.max_inputfreq = INT_MAX; +		pll_lim->vco1.min_n = 0x1; +		pll_lim->vco1.max_n = 0xff; +		pll_lim->vco1.min_m = 0x1; +		if (crystal_straps == 0) { +			/* nv05 does this, nv11 doesn't, nv10 unknown */ +			if (bios->chip_version < 0x11) +				pll_lim->vco1.min_m = 0x7; +			pll_lim->vco1.max_m = 0xd; +		} else { +			if (bios->chip_version < 0x11) +				pll_lim->vco1.min_m = 0x8; +			pll_lim->vco1.max_m = 0xe; +		} +	} + +	if (!pll_lim->refclk) +		switch (crystal_straps) { +		case 0: +			pll_lim->refclk = 13500; +			break; +		case (1 << 6): +			pll_lim->refclk = 14318; +			break; +		case (1 << 22): +			pll_lim->refclk = 27000; +			break; +		case (1 << 22 | 1 << 6): +			pll_lim->refclk = 25000; +			break; +		} + +#if 1 /* for easy debugging */ +	DRM_INFO("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq); +	DRM_INFO("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq); +	DRM_INFO("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq); +	DRM_INFO("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq); + +	DRM_INFO("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq); +	DRM_INFO("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq); +	DRM_INFO("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq); +	DRM_INFO("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq); + +	DRM_INFO("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n); +	DRM_INFO("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n); +	DRM_INFO("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m); +	DRM_INFO("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m); +	DRM_INFO("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n); +	DRM_INFO("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n); +	DRM_INFO("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m); +	DRM_INFO("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m); + +	DRM_INFO("pll.unk1c: %d\n", pll_lim->unk1c); +	DRM_INFO("pll.max_log2p_bias: %d\n", pll_lim->max_log2p_bias); +	DRM_INFO("pll.log2p_bias: %d\n", pll_lim->log2p_bias); + +	DRM_INFO("pll.refclk: %d\n", pll_lim->refclk); +#endif + +	return true; +} diff --git a/linux-core/nouveau_bios.h b/linux-core/nouveau_bios.h new file mode 100644 index 00000000..e33ecd0f --- /dev/null +++ b/linux-core/nouveau_bios.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2005-2006 Erik Waling + * Copyright (C) 2006 Stephane Marchesin + * Copyright (C) 2007-2008 Stuart Bennett + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NOUVEAU_BIOS_H__ +#define __NOUVEAU_BIOS_H__ + +#include "drmP.h" +#include "drm.h" + +#define LOC_ON_CHIP 0 + +enum dcb_output_type {/* matches DCB types */ +	DCB_OUTPUT_NONE = 4, +	DCB_OUTPUT_ANALOG = 0, +	DCB_OUTPUT_TMDS = 2, +	DCB_OUTPUT_LVDS = 3, +	DCB_OUTPUT_TV = 1, +}; + +struct bios { +	uint8_t *data; +	unsigned int length; +	bool execute; + +	uint8_t major_version, chip_version; +	uint8_t feature_byte; + +	uint32_t fmaxvco, fminvco; + +	uint32_t dactestval; + +	uint16_t init_script_tbls_ptr; +	uint16_t extra_init_script_tbl_ptr; +	uint16_t macro_index_tbl_ptr; +	uint16_t macro_tbl_ptr; +	uint16_t condition_tbl_ptr; +	uint16_t io_condition_tbl_ptr; +	uint16_t io_flag_condition_tbl_ptr; +	uint16_t init_function_tbl_ptr; + +	uint16_t pll_limit_tbl_ptr; +	uint16_t ram_restrict_tbl_ptr; + +	struct { +		struct nouveau_hw_mode *native_mode; +		uint8_t *edid; +		uint16_t lvdsmanufacturerpointer; +		uint16_t xlated_entry; +		bool power_off_for_reset; +		bool reset_after_pclk_change; +		bool dual_link; +		bool link_c_increment; +		bool if_is_24bit; +		bool BITbit1; +		int duallink_transition_clk; +		/* lower nibble stores PEXTDEV_BOOT_0 strap +		 * upper nibble stores xlated display strap */ +		uint8_t strapping; +	} fp; + +	struct { +		uint16_t output0_script_ptr; +		uint16_t output1_script_ptr; +	} tmds; + +	struct { +		uint16_t mem_init_tbl_ptr; +		uint16_t sdr_seq_tbl_ptr; +		uint16_t ddr_seq_tbl_ptr; + +		struct { +			uint8_t crt, tv, panel; +		} i2c_indices; +	} legacy; +}; + +struct dcb_entry { +	int index; +	uint8_t type; +	uint8_t i2c_index; +	uint8_t heads; +	uint8_t bus; +	uint8_t location; +	uint8_t or; +	bool duallink_possible; +	union { +		struct { +			bool use_straps_for_mode; +			bool use_power_scripts; +		} lvdsconf; +	}; +}; + +/* changing these requires matching changes to reg tables in nv_get_clock */ +#define MAX_PLL_TYPES	4 +enum pll_types { +	NVPLL, +	MPLL, +	VPLL1, +	VPLL2 +}; + +struct pll_lims { +	struct { +		int minfreq; +		int maxfreq; +		int min_inputfreq; +		int max_inputfreq; + +		uint8_t min_m; +		uint8_t max_m; +		uint8_t min_n; +		uint8_t max_n; +	} vco1, vco2; + +	uint8_t unk1c; +	uint8_t max_log2p_bias; +	uint8_t log2p_bias; +	int refclk; +}; + +bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim); +int nouveau_parse_bios(struct drm_device *dev); + +#endif /* __NOUVEAU_BIOS_H__ */ diff --git a/linux-core/nouveau_drv.c b/linux-core/nouveau_drv.c index c8f57dff..04f002f2 100644 --- a/linux-core/nouveau_drv.c +++ b/linux-core/nouveau_drv.c @@ -28,6 +28,9 @@  #include "drm_pciids.h" +unsigned int nouveau_modeset = 0; /* kms */ +module_param_named(modeset, nouveau_modeset, int, 0400); +  static struct pci_device_id pciidlist[] = {  	{  		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), @@ -104,6 +107,10 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent)  static int __init nouveau_init(void)  {  	driver.num_ioctls = nouveau_max_ioctl; + +	if (nouveau_modeset == 1) +		driver.driver_features |= DRIVER_MODESET; +  	return drm_init(&driver, pciidlist);  } diff --git a/linux-core/nv50_connector.c b/linux-core/nv50_connector.c new file mode 100644 index 00000000..d13622b7 --- /dev/null +++ b/linux-core/nv50_connector.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_connector.h" + +static struct nv50_output *nv50_connector_to_output(struct nv50_connector *connector, bool digital) +{ +	struct nv50_display *display = nv50_get_display(connector->dev); +	struct nv50_output *output = NULL; +	bool digital_possible = false; +	bool analog_possible = false; + +	switch (connector->type) { +		case CONNECTOR_VGA: +		case CONNECTOR_TV: +			analog_possible = true; +			break; +		case CONNECTOR_DVI_I: +			analog_possible = true; +			digital_possible = true; +			break; +		case CONNECTOR_DVI_D: +		case CONNECTOR_LVDS: +			digital_possible = true; +			break; +		default: +			break; +	} + +	/* Return early on bad situations. */ +	if (!analog_possible && !digital_possible) +		return NULL; + +	if (!analog_possible && !digital) +		return NULL; + +	if (!digital_possible && digital) +		return NULL; + +	list_for_each_entry(output, &display->outputs, head) { +		if (connector->bus != output->bus) +			continue; +		if (digital && output->type == OUTPUT_TMDS) +			return output; +		if (digital && output->type == OUTPUT_LVDS) +			return output; +		if (!digital && output->type == OUTPUT_DAC) +			return output; +		if (!digital && output->type == OUTPUT_TV) +			return output; +	} + +	return NULL; +} + +static bool nv50_connector_detect(struct nv50_connector *connector) +{ +	/* kindly borrrowed from the intel driver, hope it works. */ +	uint8_t out_buf[] = { 0x0, 0x0}; +	uint8_t buf[2]; +	int ret; +	struct i2c_msg msgs[] = { +		{ +			.addr = 0x50, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = 0x50, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = buf, +		} +	}; + +	NV50_DEBUG("\n"); + +	if (!connector->i2c_chan) +		return false; + +	ret = i2c_transfer(&connector->i2c_chan->adapter, msgs, 2); +	DRM_INFO("I2C detect returned %d\n", ret); + +	if (ret == 2) +		return true; + +	return false; +} + +static int nv50_connector_destroy(struct nv50_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !connector) +		return -EINVAL; + +	list_del(&connector->head); + +	if (connector->i2c_chan) +		nv50_i2c_channel_destroy(connector->i2c_chan); + +	if (dev_priv->free_connector) +		dev_priv->free_connector(connector); + +	return 0; +} + +int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type) +{ +	struct nv50_connector *connector = NULL; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = NULL; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_connector) +		connector = dev_priv->alloc_connector(dev); + +	if (!connector) +		return -ENOMEM; + +	connector->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) +		goto out; + +	if (type == CONNECTOR_UNKNOWN) +		goto out; + +	list_add_tail(&connector->head, &display->connectors); + +	connector->bus = bus; +	connector->type = type; + +	switch (type) { +		case CONNECTOR_VGA: +			DRM_INFO("Detected a VGA connector\n"); +			break; +		case CONNECTOR_DVI_D: +			DRM_INFO("Detected a DVI-D connector\n"); +			break; +		case CONNECTOR_DVI_I: +			DRM_INFO("Detected a DVI-I connector\n"); +			break; +		case CONNECTOR_LVDS: +			DRM_INFO("Detected a LVDS connector\n"); +			break; +		case CONNECTOR_TV: +			DRM_INFO("Detected a TV connector\n"); +			break; +		default: +			DRM_ERROR("Unknown connector, this is not good.\n"); +			break; +	} + +	/* some reasonable defaults */ +	if (type == CONNECTOR_DVI_D || type == CONNECTOR_LVDS) +		connector->scaling_mode = SCALE_FULLSCREEN; +	else +		connector->scaling_mode = SCALE_PANEL; + +	if (i2c_index < 0xf) +		connector->i2c_chan = nv50_i2c_channel_create(dev, i2c_index); + +	/* set function pointers */ +	connector->detect = nv50_connector_detect; +	connector->destroy = nv50_connector_destroy; +	connector->to_output = nv50_connector_to_output; + +	return 0; + +out: +	if (dev_priv->free_connector) +		dev_priv->free_connector(connector); + +	return -EINVAL; +} diff --git a/linux-core/nv50_connector.h b/linux-core/nv50_connector.h new file mode 100644 index 00000000..c70d6ef4 --- /dev/null +++ b/linux-core/nv50_connector.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_CONNECTOR_H__ +#define __NV50_CONNECTOR_H__ + +#include "nv50_output.h" +#include "nv50_i2c.h" + +#define CONNECTOR_UNKNOWN 0 +#define CONNECTOR_VGA 1 +#define CONNECTOR_DVI_D 2 +#define CONNECTOR_DVI_I 3 +#define CONNECTOR_LVDS 4 +#define CONNECTOR_TV 5 + +struct nv50_connector { +	struct list_head head; + +	struct drm_device *dev; +	int type; + +	int bus; +	struct nv50_i2c_channel *i2c_chan; +	struct nv50_output *output; + +	int scaling_mode; +	bool digital; /* last connected output, this has to be set from the outside*/ + +	bool (*detect) (struct nv50_connector *connector); +	int (*destroy) (struct nv50_connector *connector); +	struct nv50_output *(*to_output) (struct nv50_connector *connector, bool digital); +}; + +int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type); + +#endif /* __NV50_CONNECTOR_H__ */ diff --git a/linux-core/nv50_crtc.c b/linux-core/nv50_crtc.c new file mode 100644 index 00000000..974f5202 --- /dev/null +++ b/linux-core/nv50_crtc.c @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_crtc.h" +#include "nv50_cursor.h" +#include "nv50_lut.h" +#include "nv50_fb.h" + +static int nv50_crtc_validate_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode) +{ +	NV50_DEBUG("\n"); + +	if (mode->clock > 400000) +		return MODE_CLOCK_HIGH; + +	if (mode->clock < 25000) +		return MODE_CLOCK_LOW; + +	return MODE_OK; +} + +static int nv50_crtc_set_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode) +{ +	struct nouveau_hw_mode *hw_mode = crtc->mode; +	uint8_t rval; + +	NV50_DEBUG("index %d\n", crtc->index); + +	if (!mode) { +		DRM_ERROR("No mode\n"); +		return MODE_NOMODE; +	} + +	if ((rval = crtc->validate_mode(crtc, mode))) { +		DRM_ERROR("Mode invalid\n"); +		return rval; +	} + +	/* copy values to mode */ +	*hw_mode = *mode; + +	return 0; +} + +static int nv50_crtc_execute_mode(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	struct nouveau_hw_mode *hw_mode; +	uint32_t hsync_dur,  vsync_dur, hsync_start_to_end, vsync_start_to_end; +	uint32_t hunk1, vunk1, vunk2a, vunk2b; +	uint32_t offset = crtc->index * 0x400; +	uint32_t pitch; + +	NV50_DEBUG("index %d\n", crtc->index); +	NV50_DEBUG("%s native mode\n", crtc->use_native_mode ? "using" : "not using"); + +	if (crtc->use_native_mode) +		hw_mode = crtc->native_mode; +	else +		hw_mode = crtc->mode; + +	hsync_dur = hw_mode->hsync_end - hw_mode->hsync_start; +	vsync_dur = hw_mode->vsync_end - hw_mode->vsync_start; +	hsync_start_to_end = hw_mode->hblank_end - hw_mode->hsync_start; +	vsync_start_to_end = hw_mode->vblank_end - hw_mode->vsync_start; +	/* I can't give this a proper name, anyone else can? */ +	hunk1 = hw_mode->htotal - hw_mode->hsync_start + hw_mode->hblank_start; +	vunk1 = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start; +	/* Another strange value, this time only for interlaced modes. */ +	vunk2a = 2*hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start; +	vunk2b = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_end; + +	if (hw_mode->flags & V_INTERLACE) { +		vsync_dur /= 2; +		vsync_start_to_end  /= 2; +		vunk1 /= 2; +		vunk2a /= 2; +		vunk2b /= 2; +		/* magic */ +		if (hw_mode->flags & V_DBLSCAN) { +			vsync_start_to_end -= 1; +			vunk1 -= 1; +			vunk2a -= 1; +			vunk2b -= 1; +		} +	} + +	OUT_MODE(NV50_CRTC0_CLOCK + offset, hw_mode->clock | 0x800000); +	OUT_MODE(NV50_CRTC0_INTERLACE + offset, (hw_mode->flags & V_INTERLACE) ? 2 : 0); +	OUT_MODE(NV50_CRTC0_DISPLAY_START + offset, 0); +	OUT_MODE(NV50_CRTC0_UNK82C + offset, 0); +	OUT_MODE(NV50_CRTC0_DISPLAY_TOTAL + offset, hw_mode->vtotal << 16 | hw_mode->htotal); +	OUT_MODE(NV50_CRTC0_SYNC_DURATION + offset, (vsync_dur - 1) << 16 | (hsync_dur - 1)); +	OUT_MODE(NV50_CRTC0_SYNC_START_TO_BLANK_END + offset, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1)); +	OUT_MODE(NV50_CRTC0_MODE_UNK1 + offset, (vunk1 - 1) << 16 | (hunk1 - 1)); +	if (hw_mode->flags & V_INTERLACE) { +		OUT_MODE(NV50_CRTC0_MODE_UNK2 + offset, (vunk2b - 1) << 16 | (vunk2a - 1)); +	} +	OUT_MODE(NV50_CRTC0_FB_SIZE + offset, crtc->fb->height << 16 | crtc->fb->width); + +	/* I suspect this flag indicates a linear fb. */ +	pitch = ((crtc->fb->width + 63) & ~63) * (crtc->fb->bpp)/8; +	NV50_DEBUG("fb_pitch %d\n", pitch); +	OUT_MODE(NV50_CRTC0_FB_PITCH + offset, pitch | 0x100000); + +	switch (crtc->fb->depth) { +		case 8: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_8BPP);  +			break; +		case 15: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_15BPP); +			break; +		case 16: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_16BPP); +			break; +		case 24: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_24BPP);  +			break; +	} +	crtc->set_dither(crtc); +	OUT_MODE(NV50_CRTC0_COLOR_CTRL + offset, NV50_CRTC_COLOR_CTRL_MODE_COLOR); +	OUT_MODE(NV50_CRTC0_FB_POS + offset, (crtc->fb->y << 16) | (crtc->fb->x)); +	/* This is the actual resolution of the mode. */ +	OUT_MODE(NV50_CRTC0_REAL_RES + offset, (crtc->mode->vdisplay << 16) | crtc->mode->hdisplay); +	OUT_MODE(NV50_CRTC0_SCALE_CENTER_OFFSET + offset, NV50_CRTC_SCALE_CENTER_OFFSET_VAL(0,0)); + +	/* Maybe move this as well? */ +	crtc->blank(crtc, FALSE); + +	return 0; +} + +static int nv50_crtc_blank(struct nv50_crtc *crtc, bool blanked) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("index %d\n", crtc->index); +	NV50_DEBUG("%s\n", blanked ? "blanked" : "unblanked"); + +	/* We really need a framebuffer. */ +	if (!crtc->fb->block && !blanked) { +		DRM_ERROR("No framebuffer available on crtc %d\n", crtc->index); +		return -EINVAL; +	} + +	if (blanked) { +		crtc->cursor->hide(crtc); + +		OUT_MODE(NV50_CRTC0_CLUT_MODE + offset, NV50_CRTC0_CLUT_MODE_BLANK); +		OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, 0); +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_BLANK); +		OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_BLANK); +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_BLANK); +	} else { +		uint32_t ram_amount; + +		OUT_MODE(NV50_CRTC0_FB_OFFSET + offset, crtc->fb->block->start >> 8); +		OUT_MODE(0x864 + offset, 0); +		/* maybe this needs to be moved. */ +		NV_WRITE(NV50_PDISPLAY_UNK_380, 0); +		/* RAM is clamped to 256 MiB. */ +		ram_amount = nouveau_mem_fb_amount(crtc->dev); +		NV50_DEBUG("ram_amount %d\n", ram_amount); +		if (ram_amount > 256*1024*1024) +			ram_amount = 256*1024*1024; +		NV_WRITE(NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1); +		NV_WRITE(NV50_PDISPLAY_UNK_388, 0x150000); +		NV_WRITE(NV50_PDISPLAY_UNK_38C, 0); +		if (crtc->cursor->block) +			OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + offset, crtc->cursor->block->start >> 8); +		else +			OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + offset, 0); +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_UNBLANK); + +		if (crtc->cursor->visible) +			crtc->cursor->show(crtc); +		else +			crtc->cursor->hide(crtc); + +		OUT_MODE(NV50_CRTC0_CLUT_MODE + offset,  +			crtc->fb->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON); +		OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, crtc->lut->block->start >> 8); +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_UNBLANK); +		OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_UNBLANK); +	} + +	return 0; +} + +static int nv50_crtc_set_dither(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("\n"); + +	OUT_MODE(NV50_CRTC0_DITHERING_CTRL + offset, crtc->use_dithering ?  +			NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF); + +	return 0; +} + +static void nv50_crtc_calc_scale(struct nv50_crtc *crtc, uint32_t *outX, uint32_t *outY) +{ +	float hor_scale, ver_scale; + +	hor_scale = (float)crtc->native_mode->hdisplay/(float)crtc->mode->hdisplay; +	ver_scale = (float)crtc->native_mode->vdisplay/(float)crtc->mode->vdisplay; + +	if (ver_scale > hor_scale) { +		*outX = crtc->mode->hdisplay * hor_scale; +		*outY = crtc->mode->vdisplay * hor_scale; +	} else { +		*outX = crtc->mode->hdisplay * ver_scale; +		*outY = crtc->mode->vdisplay * ver_scale; +	} +} + +static int nv50_crtc_set_scale(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; +	uint32_t outX, outY; + +	NV50_DEBUG("\n"); + +	switch (crtc->scaling_mode) { +		case SCALE_ASPECT: +			nv50_crtc_calc_scale(crtc, &outX, &outY); +			break; +		case SCALE_FULLSCREEN: +			outX = crtc->native_mode->hdisplay; +			outY = crtc->native_mode->vdisplay; +			break; +		case SCALE_NOSCALE: +		case SCALE_PANEL: +		default: +			outX = crtc->mode->hdisplay; +			outY = crtc->mode->vdisplay; +			break; +	} + +	/* Got a better name for SCALER_ACTIVE? */ +	/* One day i've got to really figure out why this is needed. */ +	if ((crtc->mode->flags & V_DBLSCAN) || (crtc->mode->flags & V_INTERLACE) || +		crtc->mode->hdisplay != outX || crtc->mode->vdisplay != outY) { +		OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE); +	} else { +		OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE); +	} + +	OUT_MODE(NV50_CRTC0_SCALE_RES1 + offset, outY << 16 | outX); +	OUT_MODE(NV50_CRTC0_SCALE_RES2 + offset, outY << 16 | outX); + +	return 0; +} + +static int nv50_crtc_calc_clock(struct nv50_crtc *crtc,  +	uint32_t *bestN1, uint32_t *bestN2, uint32_t *bestM1, uint32_t *bestM2, uint32_t *bestlog2P) +{ +	struct nouveau_hw_mode *hw_mode; +	struct pll_lims limits; +	int clk, vco2, crystal; +	int minvco1, minvco2, minU1, maxU1, minU2, maxU2, minM1, maxM1; +	int maxvco1, maxvco2, minN1, maxN1, minM2, maxM2, minN2, maxN2; +	bool fixedgain2; +	int M1, N1, M2, N2, log2P; +	int clkP, calcclk1, calcclk2, calcclkout; +	int delta, bestdelta = INT_MAX; +	int bestclk = 0; + +	NV50_DEBUG("\n"); + +	if (crtc->use_native_mode) +		hw_mode = crtc->native_mode; +	else +		hw_mode = crtc->mode; + +	clk = hw_mode->clock; + +	/* These are in the g80 bios tables, at least in mine. */ +	if (!get_pll_limits(crtc->dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits)) +		return -EINVAL; + +	minvco1 = limits.vco1.minfreq, maxvco1 = limits.vco1.maxfreq; +	minvco2 = limits.vco2.minfreq, maxvco2 = limits.vco2.maxfreq; +	minU1 = limits.vco1.min_inputfreq, minU2 = limits.vco2.min_inputfreq; +	maxU1 = limits.vco1.max_inputfreq, maxU2 = limits.vco2.max_inputfreq; +	minM1 = limits.vco1.min_m, maxM1 = limits.vco1.max_m; +	minN1 = limits.vco1.min_n, maxN1 = limits.vco1.max_n; +	minM2 = limits.vco2.min_m, maxM2 = limits.vco2.max_m; +	minN2 = limits.vco2.min_n, maxN2 = limits.vco2.max_n; +	crystal = limits.refclk; +	fixedgain2 = (minM2 == maxM2 && minN2 == maxN2); + +	vco2 = (maxvco2 - maxvco2/200) / 2; +	for (log2P = 0; clk && log2P < 6 && clk <= (vco2 >> log2P); log2P++) /* log2P is maximum of 6 */ +		; +	clkP = clk << log2P; + +	if (maxvco2 < clk + clk/200)	/* +0.5% */ +		maxvco2 = clk + clk/200; + +	for (M1 = minM1; M1 <= maxM1; M1++) { +		if (crystal/M1 < minU1) +			return bestclk; +		if (crystal/M1 > maxU1) +			continue; + +		for (N1 = minN1; N1 <= maxN1; N1++) { +			calcclk1 = crystal * N1 / M1; +			if (calcclk1 < minvco1) +				continue; +			if (calcclk1 > maxvco1) +				break; + +			for (M2 = minM2; M2 <= maxM2; M2++) { +				if (calcclk1/M2 < minU2) +					break; +				if (calcclk1/M2 > maxU2) +					continue; + +				/* add calcclk1/2 to round better */ +				N2 = (clkP * M2 + calcclk1/2) / calcclk1; +				if (N2 < minN2) +					continue; +				if (N2 > maxN2) +					break; + +				if (!fixedgain2) { +					calcclk2 = calcclk1 * N2 / M2; +					if (calcclk2 < minvco2) +						break; +					if (calcclk2 > maxvco2) +						continue; +				} else +					calcclk2 = calcclk1; + +				calcclkout = calcclk2 >> log2P; +				delta = abs(calcclkout - clk); +				/* we do an exhaustive search rather than terminating +				 * on an optimality condition... +				 */ +				if (delta < bestdelta) { +					bestdelta = delta; +					bestclk = calcclkout; +					*bestN1 = N1; +					*bestN2 = N2; +					*bestM1 = M1; +					*bestM2 = M2; +					*bestlog2P = log2P; +					if (delta == 0)	/* except this one */ +						return bestclk; +				} +			} +		} +	} + +	return bestclk; +} + +static int nv50_crtc_set_clock(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index); + +	uint32_t N1 = 0, N2 = 0, M1 = 0, M2 = 0, log2P = 0; + +	uint32_t reg1 = NV_READ(pll_reg + 4); +	uint32_t reg2 = NV_READ(pll_reg + 8); + +	NV50_DEBUG("\n"); + +	NV_WRITE(pll_reg, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED | 0x10000011); + +	/* The other bits are typically empty, but let's be on the safe side. */ +	reg1 &= 0xff00ff00; +	reg2 &= 0x8000ff00; + +	if (!nv50_crtc_calc_clock(crtc, &N1, &N2, &M1, &M2, &log2P)) +		return -EINVAL; + +	NV50_DEBUG("N1 %d N2 %d M1 %d M2 %d log2P %d\n", N1, N2, M1, M2, log2P); + +	reg1 |= (M1 << 16) | N1; +	reg2 |= (log2P << 28) | (M2 << 16) | N2; + +	NV_WRITE(pll_reg + 4, reg1); +	NV_WRITE(pll_reg + 8, reg2); + +	return 0; +} + +static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	/* This acknowledges a clock request. */ +	NV_WRITE(NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(crtc->index), 0); + +	return 0; +} + +static int nv50_crtc_destroy(struct nv50_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !crtc) +		return -EINVAL; + +	list_del(&crtc->head); + +	nv50_fb_destroy(crtc); +	nv50_lut_destroy(crtc); +	nv50_cursor_destroy(crtc); + +	kfree(crtc->mode); +	kfree(crtc->native_mode); + +	if (dev_priv->free_crtc) +		dev_priv->free_crtc(crtc); + +	return 0; +} + +int nv50_crtc_create(struct drm_device *dev, int index) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_crtc *crtc = NULL; +	struct nv50_display *display = NULL; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_crtc) +		crtc = dev_priv->alloc_crtc(dev); + +	if (!crtc) +		return -ENOMEM; + +	crtc->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) +		goto out; + +	list_add_tail(&crtc->head, &display->crtcs); + +	crtc->index = index; + +	crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); +	crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + +	nv50_fb_create(crtc); +	nv50_lut_create(crtc); +	nv50_cursor_create(crtc); + +	/* set function pointers */ +	crtc->validate_mode = nv50_crtc_validate_mode; +	crtc->set_mode = nv50_crtc_set_mode; +	crtc->execute_mode = nv50_crtc_execute_mode; +	crtc->blank = nv50_crtc_blank; +	crtc->set_dither = nv50_crtc_set_dither; +	crtc->set_scale = nv50_crtc_set_scale; +	crtc->set_clock = nv50_crtc_set_clock; +	crtc->set_clock_mode = nv50_crtc_set_clock_mode; +	crtc->destroy = nv50_crtc_destroy; + +	return 0; + +out: +	if (crtc->mode) +		kfree(crtc->mode); +	if (crtc->native_mode) +		kfree(crtc->native_mode); +	if (dev_priv->free_crtc) +		dev_priv->free_crtc(crtc); + +	return -EINVAL; +} diff --git a/linux-core/nv50_crtc.h b/linux-core/nv50_crtc.h new file mode 100644 index 00000000..5eb815a5 --- /dev/null +++ b/linux-core/nv50_crtc.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_CRTC_H__ +#define __NV50_CRTC_H__ + +#include "nv50_display.h" + +struct nv50_cursor; +struct nv50_lut; +struct nv50_fb; + +struct nv50_crtc { +	struct list_head head; + +	struct drm_device *dev; +	int index; +	bool active; + +	struct nouveau_hw_mode *mode; +	struct nouveau_hw_mode *native_mode; + +	bool use_native_mode; +	bool use_dithering; +	int scaling_mode; + +	struct nv50_cursor *cursor; +	struct nv50_lut *lut; +	struct nv50_fb *fb; + +	int (*validate_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode); +	int (*set_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode); +	int (*execute_mode) (struct nv50_crtc *crtc); +	int (*blank) (struct nv50_crtc *crtc, bool blanked); +	int (*set_dither) (struct nv50_crtc *crtc); +	int (*set_scale) (struct nv50_crtc *crtc); +	int (*set_clock) (struct nv50_crtc *crtc); +	int (*set_clock_mode) (struct nv50_crtc *crtc); +	int (*destroy) (struct nv50_crtc *crtc); +}; + +int nv50_crtc_create(struct drm_device *dev, int index); + +#endif /* __NV50_CRTC_H__ */ diff --git a/linux-core/nv50_cursor.c b/linux-core/nv50_cursor.c new file mode 100644 index 00000000..3d35b936 --- /dev/null +++ b/linux-core/nv50_cursor.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_cursor.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_cursor_enable(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0x2000); +	while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK); + +	NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON); +	while((NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK) +		!= NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE); + +	crtc->cursor->enabled = true; + +	return 0; +} + +static int nv50_cursor_disable(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0); +	while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK); + +	crtc->cursor->enabled = false; + +	return 0; +} + +/* Calling update or changing the stored cursor state is left to the higher level ioctl's. */ +static int nv50_cursor_show(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("\n"); + +	/* Better not show the cursor when we have none. */ +	/* TODO: is cursor offset actually set? */ +	if (!crtc->cursor->block) { +		DRM_ERROR("No cursor available on crtc %d\n", crtc->index); +		return -EINVAL; +	} + +	OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_SHOW); + +	return 0; +} + +static int nv50_cursor_hide(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("\n"); + +	OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_HIDE); + +	return 0; +} + +static int nv50_cursor_set_pos(struct nv50_crtc *crtc, int x, int y) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV_WRITE(NV50_HW_CURSOR_POS(crtc->index), ((y & 0xFFFF) << 16) | (x & 0xFFFF)); +	/* Needed to make the cursor move. */ +	NV_WRITE(NV50_HW_CURSOR_POS_CTRL(crtc->index), 0); + +	return 0; +} + +static int nv50_cursor_set_bo(struct nv50_crtc *crtc, drm_handle_t handle) +{ +	struct mem_block *block = NULL; +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	block = find_block_by_handle(dev_priv->fb_heap, handle); + +	if (block) { +		bool first_time = false; +		if (!crtc->cursor->block) +			first_time = true; + +		crtc->cursor->block = block; + +		/* set the cursor offset cursor */ +		if (first_time) { +			OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + crtc->index * 0x400, crtc->cursor->block->start >> 8); +			if (crtc->cursor->visible) +				crtc->cursor->show(crtc); +		} +	} else { +		return -EINVAL; +	} + +	return 0; +} + +int nv50_cursor_create(struct nv50_crtc *crtc) +{ +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	crtc->cursor = kzalloc(sizeof(struct nv50_cursor), GFP_KERNEL); + +	/* function pointers */ +	crtc->cursor->show = nv50_cursor_show; +	crtc->cursor->hide = nv50_cursor_hide; +	crtc->cursor->set_pos = nv50_cursor_set_pos; +	crtc->cursor->set_bo = nv50_cursor_set_bo; +	crtc->cursor->enable = nv50_cursor_enable; +	crtc->cursor->disable = nv50_cursor_disable; + +	/* defaults */ +	crtc->cursor->visible = true; /* won't happen until there is a cursor bo */ + +	return 0; +} + +int nv50_cursor_destroy(struct nv50_crtc *crtc) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	if (crtc->cursor->enabled) { +		rval = crtc->cursor->disable(crtc); +		if (rval != 0) +			return rval; +	} + +	kfree(crtc->cursor); +	crtc->cursor = NULL; + +	return 0; +} diff --git a/linux-core/nv50_cursor.h b/linux-core/nv50_cursor.h new file mode 100644 index 00000000..a2e4632c --- /dev/null +++ b/linux-core/nv50_cursor.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_CURSOR_H__ +#define __NV50_CURSOR_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_cursor { +	struct mem_block *block; +	int x, y; +	bool visible; +	bool enabled; + +	int (*show) (struct nv50_crtc *crtc); +	int (*hide) (struct nv50_crtc *crtc); +	int (*set_pos) (struct nv50_crtc *crtc, int x, int y); +	int (*set_bo) (struct nv50_crtc *crtc, drm_handle_t handle); +	int (*enable) (struct nv50_crtc *crtc); +	int (*disable) (struct nv50_crtc *crtc); +}; + +int nv50_cursor_create(struct nv50_crtc *crtc); +int nv50_cursor_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_CURSOR_H__ */ diff --git a/linux-core/nv50_dac.c b/linux-core/nv50_dac.c new file mode 100644 index 00000000..f827fed4 --- /dev/null +++ b/linux-core/nv50_dac.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_output.h" + +static int nv50_dac_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode) +{ +	NV50_DEBUG("\n"); + +	if (mode->clock > 400000)  +		return MODE_CLOCK_HIGH; + +	if (mode->clock < 25000) +		return MODE_CLOCK_LOW; + +	return MODE_OK; +} + +static int nv50_dac_execute_mode(struct nv50_output *output, bool disconnect) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	struct nv50_crtc *crtc = output->crtc; +	struct nouveau_hw_mode *desired_mode = NULL; + +	uint32_t offset = nv50_output_or_offset(output) * 0x80; + +	uint32_t mode_ctl = NV50_DAC_MODE_CTRL_OFF; +	uint32_t mode_ctl2 = 0; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	if (disconnect) { +		NV50_DEBUG("Disconnecting DAC\n"); +		OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl); +		return 0; +	} + +	desired_mode = (crtc->use_native_mode ? crtc->native_mode : +									crtc->mode); + +	if (crtc->index == 1) +		mode_ctl |= NV50_DAC_MODE_CTRL_CRTC1; +	else +		mode_ctl |= NV50_DAC_MODE_CTRL_CRTC0; + +	/* Lacking a working tv-out, this is not a 100% sure. */ +	if (output->type == OUTPUT_DAC) { +		mode_ctl |= 0x40; +	} else if (output->type == OUTPUT_TV) { +		mode_ctl |= 0x100; +	} + +	if (desired_mode->flags & V_NHSYNC) +		mode_ctl2 |= NV50_DAC_MODE_CTRL2_NHSYNC; + +	if (desired_mode->flags & V_NVSYNC) +		mode_ctl2 |= NV50_DAC_MODE_CTRL2_NVSYNC; + +	OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl); +	OUT_MODE(NV50_DAC0_MODE_CTRL2 + offset, mode_ctl2); + +	return 0; +} + +static int nv50_dac_set_clock_mode(struct nv50_output *output) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	NV_WRITE(NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(nv50_output_or_offset(output)),  0); + +	return 0; +} + +static int nv50_dac_destroy(struct nv50_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !output) +		return -EINVAL; + +	list_del(&output->head); + +	kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); + +	return 0; +} + +int nv50_dac_create(struct drm_device *dev, int dcb_entry) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_output *output = NULL; +	struct nv50_display *display = NULL; +	struct dcb_entry *entry = NULL; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_output) +		output = dev_priv->alloc_output(dev); + +	if (!output) +		return -ENOMEM; + +	output->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) +		goto out; + +	entry = &dev_priv->dcb_table.entry[dcb_entry]; +	if (!entry) +		goto out; + +	switch (entry->type) { +		case DCB_OUTPUT_ANALOG: +			output->type = OUTPUT_DAC; +			DRM_INFO("Detected a DAC output\n"); +			break; +		default: +			goto out; +	} + +	output->dcb_entry = dcb_entry; +	output->bus = entry->bus; + +	list_add_tail(&output->head, &display->outputs); + +	output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + +	/* Set function pointers. */ +	output->validate_mode = nv50_dac_validate_mode; +	output->execute_mode = nv50_dac_execute_mode; +	output->set_clock_mode = nv50_dac_set_clock_mode; +	output->detect = NULL; /* TODO */ +	output->destroy = nv50_dac_destroy; + +	return 0; + +out: +	if (output->native_mode) +		kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); +	return -EINVAL; +} + diff --git a/linux-core/nv50_display.c b/linux-core/nv50_display.c new file mode 100644 index 00000000..56ddeb97 --- /dev/null +++ b/linux-core/nv50_display.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_output.h" +#include "nv50_connector.h" + +static int nv50_display_pre_init(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	int i; + +	NV50_DEBUG("\n"); + +	NV_WRITE(0x00610184, NV_READ(0x00614004)); +	/* +	 * I think the 0x006101XX range is some kind of main control area that enables things. +	 */ +	/* CRTC? */ +	NV_WRITE(0x00610190 + 0 * 0x10, NV_READ(0x00616100 + 0 * 0x800)); +	NV_WRITE(0x00610190 + 1 * 0x10, NV_READ(0x00616100 + 1 * 0x800)); +	NV_WRITE(0x00610194 + 0 * 0x10, NV_READ(0x00616104 + 0 * 0x800)); +	NV_WRITE(0x00610194 + 1 * 0x10, NV_READ(0x00616104 + 1 * 0x800)); +	NV_WRITE(0x00610198 + 0 * 0x10, NV_READ(0x00616108 + 0 * 0x800)); +	NV_WRITE(0x00610198 + 1 * 0x10, NV_READ(0x00616108 + 1 * 0x800)); +	NV_WRITE(0x0061019c + 0 * 0x10, NV_READ(0x0061610c + 0 * 0x800)); +	NV_WRITE(0x0061019c + 1 * 0x10, NV_READ(0x0061610c + 1 * 0x800)); +	/* DAC */ +	NV_WRITE(0x006101d0 + 0 * 0x4, NV_READ(0x0061a000 + 0 * 0x800)); +	NV_WRITE(0x006101d0 + 1 * 0x4, NV_READ(0x0061a000 + 1 * 0x800)); +	NV_WRITE(0x006101d0 + 2 * 0x4, NV_READ(0x0061a000 + 2 * 0x800)); +	/* SOR */ +	NV_WRITE(0x006101e0 + 0 * 0x4, NV_READ(0x0061c000 + 0 * 0x800)); +	NV_WRITE(0x006101e0 + 1 * 0x4, NV_READ(0x0061c000 + 1 * 0x800)); +	/* Something not yet in use, tv-out maybe. */ +	NV_WRITE(0x006101f0 + 0 * 0x4, NV_READ(0x0061e000 + 0 * 0x800)); +	NV_WRITE(0x006101f0 + 1 * 0x4, NV_READ(0x0061e000 + 1 * 0x800)); +	NV_WRITE(0x006101f0 + 2 * 0x4, NV_READ(0x0061e000 + 2 * 0x800)); + +	for (i = 0; i < 3; i++) { +		NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i), 0x00550000 | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); +		NV_WRITE(NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i), 0x00000001); +	} + +	display->preinit_done = TRUE; + +	return 0; +} + +static int nv50_display_init(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	uint32_t val; + +	NV50_DEBUG("\n"); + +	/* The precise purpose is unknown, i suspect it has something to do with text mode. */ +	if (NV_READ(NV50_PDISPLAY_SUPERVISOR) & 0x100) { +		NV_WRITE(NV50_PDISPLAY_SUPERVISOR, 0x100); +		NV_WRITE(0x006194e8, NV_READ(0x006194e8) & ~1); +		while (NV_READ(0x006194e8) & 2); +	} + +	/* taken from nv bug #12637 */ +	NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x2b00); +	do { +		val = NV_READ(NV50_PDISPLAY_UNK200_CTRL); +		if ((val & 0x9f0000) == 0x20000) +			NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x800000); + +		if ((val & 0x3f0000) == 0x30000) +			NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x200000); +	} while (val & 0x1e0000); + +	NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE); +	NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x1000b03); +	while (!(NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x40000000)); + +	/* For the moment this is just a wrapper, which should be replaced with a real fifo at some point. */ +	OUT_MODE(NV50_UNK84, 0); +	OUT_MODE(NV50_UNK88, 0); +	OUT_MODE(NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK); +	OUT_MODE(NV50_CRTC0_UNK800, 0); +	OUT_MODE(NV50_CRTC0_DISPLAY_START, 0); +	OUT_MODE(NV50_CRTC0_UNK82C, 0); + +	/* enable clock change interrupts. */ +	NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) | 0x70); + +	display->init_done = TRUE; + +	return 0; +} + +static int nv50_display_disable(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_crtc *crtc = NULL; +	int i; + +	NV50_DEBUG("\n"); + +	list_for_each_entry(crtc, &display->crtcs, head) { +		crtc->blank(crtc, TRUE); +	} + +	display->update(display); + +	/* Almost like ack'ing a vblank interrupt, maybe in the spirit of cleaning up? */ +	list_for_each_entry(crtc, &display->crtcs, head) { +		if (crtc->active) { +			uint32_t mask; + +			if (crtc->index == 1) +				mask = NV50_PDISPLAY_SUPERVISOR_CRTC1; +			else +				mask = NV50_PDISPLAY_SUPERVISOR_CRTC0; + +			NV_WRITE(NV50_PDISPLAY_SUPERVISOR, mask); +			while (!(NV_READ(NV50_PDISPLAY_SUPERVISOR) & mask)); +		} +	} + +	NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0); +	NV_WRITE(NV50_PDISPLAY_CTRL_STATE, 0); +	while ((NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x1e0000) != 0); + +	for (i = 0; i < 2; i++) { +		while (NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i)) & NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT); +	} + +	/* disable clock change interrupts. */ +	NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) & ~0x70); + +	display->init_done = FALSE; + +	return 0; +} + +static int nv50_display_update(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	NV50_DEBUG("\n"); + +	OUT_MODE(NV50_UPDATE_DISPLAY, 0); + +	return 0; +} + +int nv50_display_create(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = kzalloc(sizeof(struct nv50_display), GFP_KERNEL); +	int i, type, output_index, bus; +	/* DAC0, DAC1, DAC2, SOR0, SOR1*/ +	int or_counter[5] = {0, 0, 0, 0, 0}; +	int i2c_index[5] = {0, 0, 0, 0, 0}; +	uint32_t bus_mask = 0; +	uint32_t bus_digital = 0, bus_analog = 0; + +	NV50_DEBUG("\n"); + +	INIT_LIST_HEAD(&display->crtcs); +	INIT_LIST_HEAD(&display->outputs); +	INIT_LIST_HEAD(&display->connectors); + +	dev_priv->display_priv = display; + +	for (i = 0; i < 2; i++) { +		nv50_crtc_create(dev, i); +	} + +	/* we setup the outputs up from the BIOS table */ +	for (i = 0 ; i < dev_priv->dcb_table.entries; i++) { +		type = dev_priv->dcb_table.entry[i].type; +		output_index = ffs(dev_priv->dcb_table.entry[i].or) - 1; +		bus = dev_priv->dcb_table.entry[i].bus; + +		switch (type) { +			case DCB_OUTPUT_TMDS: +			case DCB_OUTPUT_LVDS: +				or_counter[output_index + 3] += 1; +				i2c_index[output_index + 3] = dev_priv->dcb_table.entry[i].i2c_index; +				bus_digital |= (1 << bus); +				nv50_sor_create(dev, i); +				break; +			case DCB_OUTPUT_ANALOG: +				or_counter[output_index] += 1; +				i2c_index[output_index] = dev_priv->dcb_table.entry[i].i2c_index; +				bus_analog |= (1 << bus); +				nv50_dac_create(dev, i); +				break; +			default: +				break; +		} + +	} + +	/* setup the connectors based on the output tables. */ +	for (i = 0 ; i < dev_priv->dcb_table.entries; i++) { +		int connector_type = 0; +		type = dev_priv->dcb_table.entry[i].type; +		bus = dev_priv->dcb_table.entry[i].bus; + +		/* already done? */ +		if (bus_mask & (1 << bus)) +			continue; + +		/* only do it for supported outputs */ +		if (type != DCB_OUTPUT_ANALOG && type != DCB_OUTPUT_TMDS +			&& type != DCB_OUTPUT_LVDS) +			continue; + +		switch (type) { +			case DCB_OUTPUT_TMDS: +			case DCB_OUTPUT_ANALOG: +				if ((bus_digital & (1 << bus)) && (bus_analog & (1 << bus))) +					connector_type = CONNECTOR_DVI_I; +				else if (bus_digital & (1 << bus)) +					connector_type = CONNECTOR_DVI_D; +				else if (bus_analog & (1 << bus)) +					connector_type = CONNECTOR_VGA; +				break; +			case DCB_OUTPUT_LVDS: +				connector_type = CONNECTOR_LVDS; +				break; +			default: +				connector_type = CONNECTOR_UNKNOWN; +				break; +		} + +		if (connector_type == CONNECTOR_UNKNOWN) +			continue; + +		nv50_connector_create(dev, bus, dev_priv->dcb_table.entry[i].i2c_index, connector_type); + +		bus_mask |= (1 << bus); +	} + +	display->dev = dev; + +	/* function pointers */ +	display->init = nv50_display_init; +	display->pre_init = nv50_display_pre_init; +	display->disable = nv50_display_disable; +	display->update = nv50_display_update; + +	return 0; +} + +int nv50_display_destroy(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_crtc *crtc = NULL; +	struct nv50_output *output = NULL; +	struct nv50_connector *connector = NULL; + +	NV50_DEBUG("\n"); + +	if (display->init_done) +		display->disable(display); + +	list_for_each_entry(connector, &display->connectors, head) { +		connector->destroy(connector); +	} + +	list_for_each_entry(output, &display->outputs, head) { +		output->destroy(output); +	} + +	list_for_each_entry(crtc, &display->crtcs, head) { +		crtc->destroy(crtc); +	} + +	kfree(display); +	dev_priv->display_priv = NULL; + +	return 0; +} + +/* This can be replaced with a real fifo in the future. */ +void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val) +{ +	uint32_t counter = 0; + +#if 1 +	DRM_INFO("mthd 0x%03X val 0x%08X\n", mthd, val); +#endif + +	NV_WRITE(NV50_PDISPLAY_CTRL_VAL, val); +	NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_PENDING | 0x10000 | mthd | NV50_PDISPLAY_CTRL_STATE_ENABLE); + +	while (NV_READ(NV50_PDISPLAY_CTRL_STATE) & NV50_PDISPLAY_CTRL_STATE_PENDING) { +		counter++; +		if (counter > 25000) { +			DRM_ERROR("You probably need a reboot now\n"); +			break; +		} +	} +} + +struct nv50_display *nv50_get_display(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	return (struct nv50_display *) dev_priv->display_priv; +} diff --git a/linux-core/nv50_display.h b/linux-core/nv50_display.h new file mode 100644 index 00000000..f20e67da --- /dev/null +++ b/linux-core/nv50_display.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_DISPLAY_H__ +#define __NV50_DISPLAY_H__ + +#include "drmP.h" +#include "drm.h" +#include "nouveau_dma.h" +#include "nouveau_drv.h" +#include "nouveau_reg.h" +#include "nv50_display_commands.h" + +/* for convience, so you can see through the trees. */ +#define NV50_DEBUG DRM_ERROR + +struct nouveau_hw_mode { +	unsigned int clock; +	unsigned short hdisplay, hblank_start, hsync_start, hsync_end, hblank_end, htotal; +	unsigned short vdisplay, vblank_start, vsync_start, vsync_end, vblank_end, vtotal; + +	unsigned int flags; +}; + +struct nv50_crtc; +struct nv50_output; +struct nv50_connector; + +struct nv50_display { +	struct drm_device *dev; + +	bool preinit_done; +	bool init_done; + +	int last_crtc; /* crtc used for last mode set */ + +	int (*pre_init) (struct nv50_display *display); +	int (*init) (struct nv50_display *display); +	int (*disable) (struct nv50_display *display); +	int (*update) (struct nv50_display *display); + +	struct list_head crtcs; +	struct list_head outputs; +	struct list_head connectors; +}; + +enum scaling_modes { +	SCALE_PANEL, +	SCALE_FULLSCREEN, +	SCALE_ASPECT, +	SCALE_NOSCALE, +	SCALE_INVALID +}; + +void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val); +struct nv50_display *nv50_get_display(struct drm_device *dev); +int nv50_display_create(struct drm_device *dev); +int nv50_display_destroy(struct drm_device *dev); + +#endif /* __NV50_DISPLAY_H__ */ diff --git a/linux-core/nv50_display_commands.h b/linux-core/nv50_display_commands.h new file mode 100644 index 00000000..97b3d3c1 --- /dev/null +++ b/linux-core/nv50_display_commands.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* copied from ddx definitions, until rules-ng can handle this */ + +#define NV50_UPDATE_DISPLAY		0x80 +#define NV50_UNK84				0x84 +#define NV50_UNK88				0x88 + +#define NV50_DAC0_MODE_CTRL		0x400 +	#define NV50_DAC_MODE_CTRL_OFF			(0 << 0) +	#define NV50_DAC_MODE_CTRL_CRTC0			(1 << 0) +	#define NV50_DAC_MODE_CTRL_CRTC1			(1 << 1) +#define NV50_DAC1_MODE_CTRL		0x480 +#define NV50_DAC2_MODE_CTRL		0x500 + +#define NV50_DAC0_MODE_CTRL2		0x404 +	#define NV50_DAC_MODE_CTRL2_NHSYNC			(1 << 0) +	#define NV50_DAC_MODE_CTRL2_NVSYNC			(2 << 0) +#define NV50_DAC1_MODE_CTRL2		0x484 +#define NV50_DAC2_MODE_CTRL2		0x504 + +#define NV50_SOR0_MODE_CTRL		0x600 +	#define NV50_SOR_MODE_CTRL_OFF			(0 << 0) +	#define NV50_SOR_MODE_CTRL_CRTC0			(1 << 0) +	#define NV50_SOR_MODE_CTRL_CRTC1			(1 << 1) +	#define NV50_SOR_MODE_CTRL_LVDS			(0 << 8) +	#define NV50_SOR_MODE_CTRL_TMDS			(1 << 8) +	#define NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK	(4 << 8) +	#define NV50_SOR_MODE_CTRL_NHSYNC			(1 << 12) +	#define NV50_SOR_MODE_CTRL_NVSYNC			(2 << 12) +#define NV50_SOR1_MODE_CTRL		0x640 + +#define NV50_CRTC0_UNK800			0x800 +#define NV50_CRTC0_CLOCK			0x804 +#define NV50_CRTC0_INTERLACE		0x808 + +/* 0x810 is a reasonable guess, nothing more. */ +#define NV50_CRTC0_DISPLAY_START				0x810 +#define NV50_CRTC0_DISPLAY_TOTAL				0x814 +#define NV50_CRTC0_SYNC_DURATION				0x818 +#define NV50_CRTC0_SYNC_START_TO_BLANK_END		0x81C +#define NV50_CRTC0_MODE_UNK1					0x820 +#define NV50_CRTC0_MODE_UNK2					0x824 + +#define NV50_CRTC0_UNK82C						0x82C + +/* You can't have a palette in 8 bit mode (=OFF) */ +#define NV50_CRTC0_CLUT_MODE		0x840 +	#define NV50_CRTC0_CLUT_MODE_BLANK		0x00000000 +	#define NV50_CRTC0_CLUT_MODE_OFF		0x80000000 +	#define NV50_CRTC0_CLUT_MODE_ON		0xC0000000 +#define NV50_CRTC0_CLUT_OFFSET		0x844 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC0_BLANK_UNK1		0x85C +	#define NV84_CRTC0_BLANK_UNK1_BLANK	0x0 +	#define NV84_CRTC0_BLANK_UNK1_UNBLANK	0x1 + +#define NV50_CRTC0_FB_OFFSET		0x860 + +#define NV50_CRTC0_FB_SIZE			0x868 +#define NV50_CRTC0_FB_PITCH		0x86C + +#define NV50_CRTC0_DEPTH			0x870 +	#define NV50_CRTC0_DEPTH_8BPP		0x1E00 +	#define NV50_CRTC0_DEPTH_15BPP	0xE900 +	#define NV50_CRTC0_DEPTH_16BPP	0xE800 +	#define NV50_CRTC0_DEPTH_24BPP	0xCF00 + +/* I'm openminded to better interpretations. */ +/* This is an educated guess. */ +/* NV50 has RAMDAC and TMDS offchip, so it's unlikely to be that. */ +#define NV50_CRTC0_BLANK_CTRL		0x874 +	#define NV50_CRTC0_BLANK_CTRL_BLANK	0x0 +	#define NV50_CRTC0_BLANK_CTRL_UNBLANK	0x1 + +#define NV50_CRTC0_CURSOR_CTRL	0x880 +	#define NV50_CRTC0_CURSOR_CTRL_SHOW	0x85000000 +	#define NV50_CRTC0_CURSOR_CTRL_HIDE	0x05000000 + +#define NV50_CRTC0_CURSOR_OFFSET	0x884 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC0_BLANK_UNK2		0x89C +	#define NV84_CRTC0_BLANK_UNK2_BLANK	0x0 +	#define NV84_CRTC0_BLANK_UNK2_UNBLANK	0x1 + +#define NV50_CRTC0_DITHERING_CTRL	0x8A0 +	#define NV50_CRTC0_DITHERING_CTRL_ON	0x11 +	#define NV50_CRTC0_DITHERING_CTRL_OFF	0x0 + +#define NV50_CRTC0_SCALE_CTRL		0x8A4 +	#define	NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE	(0 << 0) +	/* It doesn't seem to be needed, hence i wonder what it does precisely. */ +	#define	NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE	(9 << 0) +#define NV50_CRTC0_COLOR_CTRL		0x8A8 +	#define NV50_CRTC_COLOR_CTRL_MODE_COLOR		(4 << 16) + +#define NV50_CRTC0_FB_POS			0x8C0 +#define NV50_CRTC0_REAL_RES		0x8C8 + +/* Added a macro, because the signed stuff can cause you problems very quickly. */ +#define NV50_CRTC0_SCALE_CENTER_OFFSET		0x8D4 +	#define NV50_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF)) +/* Both of these are needed, otherwise nothing happens. */ +#define NV50_CRTC0_SCALE_RES1		0x8D8 +#define NV50_CRTC0_SCALE_RES2		0x8DC + +#define NV50_CRTC1_UNK800			0xC00 +#define NV50_CRTC1_CLOCK			0xC04 +#define NV50_CRTC1_INTERLACE		0xC08 + +/* 0xC10 is a reasonable guess, nothing more. */ +#define NV50_CRTC1_DISPLAY_START				0xC10 +#define NV50_CRTC1_DISPLAY_TOTAL				0xC14 +#define NV50_CRTC1_SYNC_DURATION				0xC18 +#define NV50_CRTC1_SYNC_START_TO_BLANK_END		0xC1C +#define NV50_CRTC1_MODE_UNK1					0xC20 +#define NV50_CRTC1_MODE_UNK2					0xC24 + +#define NV50_CRTC1_CLUT_MODE		0xC40 +	#define NV50_CRTC1_CLUT_MODE_BLANK		0x00000000 +	#define NV50_CRTC1_CLUT_MODE_OFF		0x80000000 +	#define NV50_CRTC1_CLUT_MODE_ON		0xC0000000 +#define NV50_CRTC1_CLUT_OFFSET		0xC44 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC1_BLANK_UNK1		0xC5C +	#define NV84_CRTC1_BLANK_UNK1_BLANK	0x0 +	#define NV84_CRTC1_BLANK_UNK1_UNBLANK	0x1 + +#define NV50_CRTC1_FB_OFFSET		0xC60 + +#define NV50_CRTC1_FB_SIZE			0xC68 +#define NV50_CRTC1_FB_PITCH		0xC6C + +#define NV50_CRTC1_DEPTH			0xC70 +	#define NV50_CRTC1_DEPTH_8BPP		0x1E00 +	#define NV50_CRTC1_DEPTH_15BPP	0xE900 +	#define NV50_CRTC1_DEPTH_16BPP	0xE800 +	#define NV50_CRTC1_DEPTH_24BPP	0xCF00 + +/* I'm openminded to better interpretations. */ +#define NV50_CRTC1_BLANK_CTRL		0xC74 +	#define NV50_CRTC1_BLANK_CTRL_BLANK	0x0 +	#define NV50_CRTC1_BLANK_CTRL_UNBLANK	0x1 + +#define NV50_CRTC1_CURSOR_CTRL		0xC80 +	#define NV50_CRTC1_CURSOR_CTRL_SHOW	0x85000000 +	#define NV50_CRTC1_CURSOR_CTRL_HIDE	0x05000000 + +#define NV50_CRTC1_CURSOR_OFFSET	0xC84 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC1_BLANK_UNK2		0xC9C +	#define NV84_CRTC1_BLANK_UNK2_BLANK	0x0 +	#define NV84_CRTC1_BLANK_UNK2_UNBLANK	0x1 + +#define NV50_CRTC1_DITHERING_CTRL	0xCA0 +	#define NV50_CRTC1_DITHERING_CTRL_ON	0x11 +	#define NV50_CRTC1_DITHERING_CTRL_OFF	0x0 + +#define NV50_CRTC1_SCALE_CTRL		0xCA4 +#define NV50_CRTC1_COLOR_CTRL		0xCA8 + +#define NV50_CRTC1_FB_POS			0xCC0 +#define NV50_CRTC1_REAL_RES		0xCC8 + +#define NV50_CRTC1_SCALE_CENTER_OFFSET		0xCD4 +/* Both of these are needed, otherwise nothing happens. */ +#define NV50_CRTC1_SCALE_RES1		0xCD8 +#define NV50_CRTC1_SCALE_RES2		0xCDC diff --git a/linux-core/nv50_fb.c b/linux-core/nv50_fb.c new file mode 100644 index 00000000..b44b48ab --- /dev/null +++ b/linux-core/nv50_fb.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_fb.h" +#include "nv50_lut.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_fb_bind(struct nv50_crtc *crtc, struct nv50_fb_info *info) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc || !info) { +		DRM_ERROR("crtc %p info %p\n",crtc, info); +		return -EINVAL; +	} + +	if (!info->block || !info->width || !info->height || !info->depth || !info->bpp) { +		DRM_ERROR("block %p width %d height %d depth %d bpp %d\n", info->block, info->width, info->height, info->depth, info->bpp); +		return -EINVAL; +	} + +	crtc->fb->block = info->block; +	crtc->fb->width = info->width; +	crtc->fb->height = info->height; + +	crtc->fb->y = info->x; +	crtc->fb->x = info->y; + +	crtc->fb->depth = info->depth; +	crtc->fb->bpp = info->bpp; + +	/* update lut if needed */ +	if (crtc->fb->depth != crtc->lut->depth) { +		int r_size = 0, g_size = 0, b_size = 0; +		uint16_t *r_val, *g_val, *b_val; +		int i; + +		switch (crtc->fb->depth) { +			case 15: +				r_size = 32; +				g_size = 32; +				b_size = 32; +				break; +			case 16: +				r_size = 32; +				g_size = 64; +				b_size = 32; +				break; +			case 24: +			default: +				r_size = 256; +				g_size = 256; +				b_size = 256; +				break; +		} + +		r_val = kmalloc(r_size * sizeof(uint16_t), GFP_KERNEL); +		g_val = kmalloc(g_size * sizeof(uint16_t), GFP_KERNEL); +		b_val = kmalloc(b_size * sizeof(uint16_t), GFP_KERNEL); + +		if (!r_val || !g_val || !b_val) +			return -ENOMEM; + +		/* Set the color indices. */ +		for (i = 0; i < r_size; i++) { +			r_val[i] = i << 8; +		} +		for (i = 0; i < g_size; i++) { +			g_val[i] = i << 8; +		} +		for (i = 0; i < b_size; i++) { +			b_val[i] = i << 8; +		} + +		rval = crtc->lut->set(crtc, r_val, g_val, b_val); + +		/* free before returning */ +		kfree(r_val); +		kfree(g_val); +		kfree(b_val); + +		if (rval != 0) +			return rval; +	} + +	return 0; +} + +int nv50_fb_create(struct nv50_crtc *crtc) +{ +	if (!crtc) +		return -EINVAL; + +	crtc->fb = kzalloc(sizeof(struct nv50_fb), GFP_KERNEL); + +	crtc->fb->bind = nv50_fb_bind; + +	return 0; +} + +int nv50_fb_destroy(struct nv50_crtc *crtc) +{ +	if (!crtc) +		return -EINVAL; + +	kfree(crtc->fb); +	crtc->fb = NULL; + +	return 0; +} diff --git a/linux-core/nv50_fb.h b/linux-core/nv50_fb.h new file mode 100644 index 00000000..6b286315 --- /dev/null +++ b/linux-core/nv50_fb.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_FB_H__ +#define __NV50_FB_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_fb_info { +	struct mem_block *block; +	int width, height; +	int bpp, depth; +	int x,y; +}; + +struct nv50_fb { +	struct mem_block *block; +	int width, height; +	int bpp, depth; + +	int x,y; + +	/* function points */ +	int (*bind) (struct nv50_crtc *crtc, struct nv50_fb_info *info); +}; + +int nv50_fb_create(struct nv50_crtc *crtc); +int nv50_fb_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_FB_H__ */ diff --git a/linux-core/nv50_i2c.c b/linux-core/nv50_i2c.c new file mode 100644 index 00000000..cf55645b --- /dev/null +++ b/linux-core/nv50_i2c.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* This is largely a clone from xorg i2c functions, as i had serious trouble getting an i2c_bit_algo adaptor running. */ + +#include "nv50_i2c.h" + +static void nv50_i2c_set_bits(struct nv50_i2c_channel *chan, int clock_high, int data_high) +{ +	struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + +	NV_WRITE(NV50_PCONNECTOR_I2C_PORT(chan->index), 4 | (data_high << 1) | clock_high); +} + +static void nv50_i2c_get_bits(struct nv50_i2c_channel *chan, int *clock_high, int *data_high) +{ +	struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + +	uint32_t val = NV_READ(NV50_PCONNECTOR_I2C_PORT(chan->index)); + +	if (val & 1) +		*clock_high = 1; +	else +		*clock_high = 0; + +	if (val & 2) +		*data_high = 1; +	else +		*data_high = 0; +} + +static bool nv50_i2c_raise_clock(struct nv50_i2c_channel *chan, int data) +{ +	int i, clock; + +	nv50_i2c_set_bits(chan, 1, data); +	udelay(2); + +	for (i = 2200; i > 0; i -= 2) { +		nv50_i2c_get_bits(chan, &clock, &data); +		if (clock) +			return TRUE; +		udelay(2); +	} + +	printk("a timeout occured in nv50_i2c_raise_clock\n"); + +	return FALSE; +} + +static bool nv50_i2c_start(struct nv50_i2c_channel *chan) +{ +	if (!nv50_i2c_raise_clock(chan, 1)) +		return FALSE; + +	nv50_i2c_set_bits(chan, 1, 0); +	udelay(5); + +	nv50_i2c_set_bits(chan, 0, 0); +	udelay(5); + +	return TRUE; +} + +static void nv50_i2c_stop(struct nv50_i2c_channel *chan) +{ +	nv50_i2c_set_bits(chan, 0, 0); +	udelay(2); + +	nv50_i2c_set_bits(chan, 1, 0); +	udelay(5); + +	nv50_i2c_set_bits(chan, 1, 1); +	udelay(5); +} + +static bool nv50_i2c_write_bit(struct nv50_i2c_channel *chan, int data) +{ +	bool rval; + +	nv50_i2c_set_bits(chan, 0, data); +	udelay(2); + +	rval = nv50_i2c_raise_clock(chan, data); +	udelay(5); + +	nv50_i2c_set_bits(chan, 0, data); +	udelay(5); + +	return rval; +} + +static bool nv50_i2c_read_bit(struct nv50_i2c_channel *chan, int *data) +{ +	bool rval; +	int clock; + +	rval = nv50_i2c_raise_clock(chan, 1); +	udelay(5); + +	nv50_i2c_get_bits(chan, &clock, data); +	udelay(5); + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	return rval; +} + +static bool nv50_i2c_write_byte(struct nv50_i2c_channel *chan, uint8_t byte) +{ +	bool rval; +	int i, clock, data; + +	for (i = 7; i >= 0; i--) +		if (!nv50_i2c_write_bit(chan, (byte >> i) & 1)) +			return FALSE; + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	rval = nv50_i2c_raise_clock(chan, 1); + +	if (rval) { +		for (i = 40; i > 0; i -= 2) { +			udelay(2); +			nv50_i2c_get_bits(chan, &clock, &data); +			if (data == 0)  +				break; +		} + +		if (i <= 0) { +			printk("a timeout occured in nv50_i2c_write_byte\n"); +			rval = FALSE; +		} +	} + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	return rval; +} + +static bool nv50_i2c_read_byte(struct nv50_i2c_channel *chan, uint8_t *byte, bool last) +{ +	int i, bit; + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	*byte = 0; + +	for (i = 7; i >= 0; i--) { +		if (nv50_i2c_read_bit(chan, &bit)) { +			if (bit) +				*byte |= (1 << i); +		} else { +			return FALSE; +		} +	} + +	if (!nv50_i2c_write_bit(chan, last ? 1 : 0)) +		return FALSE; + +	return TRUE; +} + +/* only 7 bits addresses. */ +static bool nv50_i2c_address(struct nv50_i2c_channel *chan, uint8_t address, bool write) +{ +	if (nv50_i2c_start(chan)) { +		uint8_t real_addr = (address << 1); +		if (!write) +			real_addr |= 1; + +		if (nv50_i2c_write_byte(chan, real_addr)) +			return TRUE; + +		/* failure, so issue stop */ +		nv50_i2c_stop(chan); +	} + +	return FALSE; +} + +static bool nv50_i2c_read(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length) +{ +	int i, j; +	bool rval, last; + +	/* retries */ +	for (i = 0; i < 4; i++) { +		rval = nv50_i2c_address(chan, address, FALSE); +		if (!rval) +			return FALSE; + +		for (j = 0; j < length; j++) { +			last = false; +			if (j == (length - 1)) +				last = true; +			rval = nv50_i2c_read_byte(chan, &buffer[j], last); +			if (!rval) { +				nv50_i2c_stop(chan); +				break; +			} +		} + +		nv50_i2c_stop(chan); + +		/* done */ +		if (rval) +			break; +	} + +	if (!rval) +		printk("nv50_i2c_read failed\n"); + +	return rval; +} + +static bool nv50_i2c_write(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length) +{ +	int i, j; +	bool rval; + +	/* retries */ +	for (i = 0; i < 4; i++) { +		rval = nv50_i2c_address(chan, address, TRUE); +		if (!rval) +			return FALSE; + +		for (j = 0; j < length; j++) { +			rval = nv50_i2c_write_byte(chan, buffer[j]); +			if (!rval) { +				break; +			} +		} + +		nv50_i2c_stop(chan); + +		/* done */ +		if (rval) +			break; +	} + +	if (!rval) +		printk("nv50_i2c_write failed\n"); + +	return rval; +} + +static int nv50_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ +	struct nv50_i2c_channel *chan = i2c_get_adapdata(i2c_adap); +	bool rval; +	int i; + +	for (i = 0; i < num; i++) { +		if (msgs[i].flags & I2C_M_RD) { /* read */ +			rval = nv50_i2c_read(chan, msgs[i].addr, msgs[i].buf, msgs[i].len); +		} else { /* write */ +			rval = nv50_i2c_write(chan, msgs[i].addr, msgs[i].buf, msgs[i].len); +		} + +		if (!rval) +			break; +	} + +	if (rval) +		return i; +	else +		return -EINVAL; +} + +static u32 nv50_i2c_functionality(struct i2c_adapter *adap) +{ +	return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm nv50_i2c_algo = { +	.master_xfer	= nv50_i2c_xfer, +	.functionality	= nv50_i2c_functionality, +}; + +static int nv50_i2c_register_bus(struct i2c_adapter *adap) +{ +	adap->algo = &nv50_i2c_algo; + +	adap->timeout = 40; +	adap->retries = 4; + +	return i2c_add_adapter(adap); +} + +#define I2C_HW_B_NOUVEAU 0x010030 +struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index) +{ +	struct nv50_i2c_channel *chan; + +	chan = kzalloc(sizeof(struct nv50_i2c_channel), GFP_KERNEL); + +	if (!chan) +		goto out; + +	DRM_INFO("Creating i2c bus with index %d\n", index); + +	chan->dev = dev; +	chan->index = index; +	snprintf(chan->adapter.name, I2C_NAME_SIZE, "nv50 i2c %d", index); +	chan->adapter.owner = THIS_MODULE; +	chan->adapter.id = I2C_HW_B_NOUVEAU; +	chan->adapter.dev.parent = &dev->pdev->dev; + +	i2c_set_adapdata(&chan->adapter, chan); + +	if (nv50_i2c_register_bus(&chan->adapter)) +		goto out; + +	return chan; + +out: +	kfree(chan); +	return NULL; +} + +void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan) +{ +	if (!chan) +		return; + +	i2c_del_adapter(&chan->adapter); +	kfree(chan); +} diff --git a/linux-core/nv50_i2c.h b/linux-core/nv50_i2c.h new file mode 100644 index 00000000..1740f8ec --- /dev/null +++ b/linux-core/nv50_i2c.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_I2C_H__ +#define __NV50_I2C_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm.h" +#include "nv50_display.h" + +struct nv50_i2c_channel { +	struct drm_device *dev; + +	uint32_t index; +	struct i2c_adapter adapter; +}; + +struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index); +void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan); + +#endif /* __NV50_I2C_H__ */ diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c new file mode 100644 index 00000000..01d4bc9a --- /dev/null +++ b/linux-core/nv50_kms_wrapper.c @@ -0,0 +1,960 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_kms_wrapper.h" +#include "drm_crtc_helper.h" /* be careful what you use from this */ + +/* This file serves as the interface between the common kernel modesetting code and the device dependent implementation. */ + +/* + * Get private functions. + */ + +struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	return dev_priv->kms_priv; +} + +/* + * Allocation functions. + */ + +static void *nv50_kms_alloc_crtc(struct drm_device *dev) +{ +	struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); +	struct nv50_kms_crtc *crtc = kzalloc(sizeof(struct nv50_kms_crtc), GFP_KERNEL); + +	list_add_tail(&crtc->head, &kms_priv->crtcs); + +	return &(crtc->priv); +} + +static void *nv50_kms_alloc_output(struct drm_device *dev) +{ +	struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); +	struct nv50_kms_encoder *encoder = kzalloc(sizeof(struct nv50_kms_encoder), GFP_KERNEL); + +	list_add_tail(&encoder->head, &kms_priv->encoders); + +	return &(encoder->priv); +} + +static void *nv50_kms_alloc_connector(struct drm_device *dev) +{ +	struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); +	struct nv50_kms_connector *connector = kzalloc(sizeof(struct nv50_kms_connector), GFP_KERNEL); + +	list_add_tail(&connector->head, &kms_priv->connectors); + +	return &(connector->priv); +} + +static void nv50_kms_free_crtc(void *crtc) +{ +	struct nv50_kms_crtc *kms_crtc = from_nv50_crtc(crtc); + +	list_del(&kms_crtc->head); + +	kfree(kms_crtc); +} + +static void nv50_kms_free_output(void *output) +{ +	struct nv50_kms_encoder *kms_encoder = from_nv50_output(output); + +	list_del(&kms_encoder->head); + +	kfree(kms_encoder); +} + +static void nv50_kms_free_connector(void *connector) +{ +	struct nv50_kms_connector *kms_connector = from_nv50_connector(connector); + +	list_del(&kms_connector->head); + +	kfree(kms_connector); +} + +/* + * Mode conversion functions. + */ + +static struct nouveau_hw_mode *nv50_kms_to_hw_mode(struct drm_display_mode *mode) +{ +	struct nouveau_hw_mode *hw_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + +	/* create hw values. */ +	hw_mode->clock = mode->clock; +	hw_mode->flags = hw_mode->flags; + +	hw_mode->hdisplay = mode->hdisplay; +	hw_mode->hsync_start = mode->hsync_start; +	hw_mode->hsync_end = mode->hsync_end; +	hw_mode->htotal = mode->htotal; + +	hw_mode->hblank_start = mode->hdisplay + 1; +	hw_mode->hblank_end = mode->htotal; + +	hw_mode->vdisplay = mode->vdisplay; +	hw_mode->vsync_start = mode->vsync_start; +	hw_mode->vsync_end = mode->vsync_end; +	hw_mode->vtotal = mode->vtotal; + +	hw_mode->vblank_start = mode->vdisplay + 1; +	hw_mode->vblank_end = mode->vtotal; + +	return hw_mode; +} + +/* + * State mirroring functions. + */ + +static void nv50_kms_mirror_routing(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_crtc *crtc = NULL; +	struct nv50_output *output = NULL; +	struct nv50_connector *connector = NULL; +	struct drm_connector *drm_connector = NULL; + +	/* Wipe all previous connections. */ +	list_for_each_entry(connector, &display->connectors, head) { +		connector->output = NULL; +	} + +	list_for_each_entry(output, &display->outputs, head) { +		output->crtc = NULL; +	} + +	list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +		if (drm_connector->encoder) { +			output = to_nv50_output(drm_connector->encoder); +			connector = to_nv50_connector(drm_connector); + +			/* hook up output to connector. */ +			connector->output = output; + +			if (drm_connector->encoder->crtc) { +				crtc = to_nv50_crtc(drm_connector->encoder->crtc); + +				/* hook up output to crtc. */ +				output->crtc = crtc; +			} +		} +	} +} + +/* + * FB functions. + */ + +static void nv50_kms_framebuffer_destroy(struct drm_framebuffer *drm_framebuffer) +{ +	drm_framebuffer_cleanup(drm_framebuffer); + +	kfree(drm_framebuffer); +} + +static const struct drm_framebuffer_funcs nv50_kms_fb_funcs = { +	.destroy = nv50_kms_framebuffer_destroy, +}; + +/* + * Mode config functions. + */ + +static struct drm_framebuffer *nv50_kms_framebuffer_create(struct drm_device *dev, +						struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd) +{ +	struct drm_framebuffer *drm_framebuffer = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL); +	if (!drm_framebuffer) +		return NULL; + +	drm_framebuffer_init(dev, drm_framebuffer, &nv50_kms_fb_funcs); +	drm_helper_mode_fill_fb_struct(drm_framebuffer, mode_cmd); + +	return drm_framebuffer; +} + +static int nv50_kms_fb_changed(struct drm_device *dev) +{ +	return 0; /* not needed until nouveaufb? */ +} + +static const struct drm_mode_config_funcs nv50_kms_mode_funcs = { +	.resize_fb = NULL, +	.fb_create = nv50_kms_framebuffer_create, +	.fb_changed = nv50_kms_fb_changed, +}; + +/* + * CRTC functions. + */ + +static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc, uint32_t buffer_handle, +			  uint32_t width, uint32_t height) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); +	struct nv50_display *display = nv50_get_display(crtc->dev); +	int rval; + +	if (width != 64 || height != 64) +		return -EINVAL; + +	rval = crtc->cursor->set_bo(crtc, (drm_handle_t) buffer_handle); + +	if (rval != 0) +		return rval; + +	/* in case this triggers any other cursor changes */ +	display->update(display); + +	return 0; +} + +static int nv50_kms_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + +	return crtc->cursor->set_pos(crtc, x, y); +} + +void nv50_kms_crtc_gamma_set(struct drm_crtc *drm_crtc, u16 *r, u16 *g, u16 *b, +		uint32_t size) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + +	if (size != 256) +		return; + +	crtc->lut->set(crtc, (uint16_t *)r, (uint16_t *)g, (uint16_t *)b); +} + +int nv50_kms_crtc_set_config(struct drm_mode_set *set) +{ +	int rval = 0, i; +	uint32_t crtc_mask = 0; +	struct drm_device *dev = NULL; +	struct drm_nouveau_private *dev_priv = NULL; +	struct nv50_display *display = NULL; +	struct drm_connector *drm_connector = NULL; +	struct drm_encoder *drm_encoder = NULL; +	struct drm_crtc *drm_crtc = NULL; + +	struct nv50_crtc *crtc = NULL; +	struct nv50_output *output = NULL; +	struct nv50_connector *connector = NULL; +	struct nouveau_hw_mode *hw_mode = NULL; +	struct nv50_fb_info fb_info; + +	NV50_DEBUG("\n"); + +	/* +	 * Initial approach is very simple, always set a mode. +	 * Always bail out completely if something is wrong. +	 * Later this could be extended to be more smart. +	 */ + +	/* Sanity checking */ +	if (!set) { +		NV50_DEBUG("Sanity check failed\n"); +		goto out; +	} + +	if (!set->crtc || !set->fb || !set->mode || !set->connectors) { +		NV50_DEBUG("Sanity check failed\n"); +		goto out; +	} + +	/* Basic variable setting */ +	dev = set->crtc->dev; +	dev_priv = dev->dev_private; +	display = nv50_get_display(dev); +	crtc = to_nv50_crtc(set->crtc); + +	/* Mode validation */ +	hw_mode = nv50_kms_to_hw_mode(set->mode); + +	rval = crtc->validate_mode(crtc, hw_mode); + +	if (rval != MODE_OK) { +		NV50_DEBUG("Mode not ok\n"); +		goto out; +	} + +	for (i = 0; i < set->num_connectors; i++) { +		drm_connector = set->connectors[i]; +		if (!drm_connector) { +			NV50_DEBUG("No connector\n"); +			goto out; +		} +		connector = to_nv50_connector(drm_connector); + +		output = connector->to_output(connector, connector->digital); +		if (!output) { +			NV50_DEBUG("No output\n"); +			goto out; +		} + +		rval = output->validate_mode(output, hw_mode); +		if (rval != MODE_OK) { +			NV50_DEBUG("Mode not ok\n"); +			goto out; +		} +	} + +	/* Validation done, move on to cleaning of existing structures. */ + +	/* find encoders that use this crtc. */ +	list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { +		if (drm_encoder->crtc == set->crtc) { +			/* find the connector that goes with it */ +			list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +				if (drm_connector->encoder == drm_encoder) { +					drm_connector->encoder =  NULL; +					break; +				} +			} +			drm_encoder->crtc = NULL; +		} +	} + +	/* now find if our desired encoders or connectors are in use already. */ +	for (i = 0; i < set->num_connectors; i++) { +		drm_connector = set->connectors[i]; +		if (!drm_connector) { +			NV50_DEBUG("No connector\n"); +			goto out; +		} + +		if (!drm_connector->encoder) +			continue; + +		drm_encoder = drm_connector->encoder; +		drm_connector->encoder = NULL; + +		if (!drm_encoder->crtc) +			continue; + +		drm_crtc = drm_encoder->crtc; +		drm_encoder->crtc = NULL; + +		crtc = to_nv50_crtc(drm_crtc); +		crtc->active = false; +		drm_crtc->enabled = false; +	} + +	/* set framebuffer */ +	set->crtc->fb = set->fb; + +	/* Time to wire up the public encoder, the private one will be handled later. */ +	for (i = 0; i < set->num_connectors; i++) { +		drm_connector = set->connectors[i]; +		if (!drm_connector) { +			NV50_DEBUG("No connector\n"); +			goto out; +		} + +		output = connector->to_output(connector, connector->digital); +		if (!output) { +			NV50_DEBUG("No output\n"); +			goto out; +		} + +		/* find the encoder public structure that matches out output structure. */ +		drm_encoder = to_nv50_kms_encoder(output); + +		if (!drm_encoder) { +			NV50_DEBUG("No encoder\n"); +			goto out; +		} + + +		drm_encoder->crtc = set->crtc; +		drm_connector->encoder = drm_encoder; +	} + +	/* mirror everything to the private structs */ +	nv50_kms_mirror_routing(dev); + +	/* set private framebuffer */ +	crtc = to_nv50_crtc(set->crtc); +	fb_info.block = find_block_by_handle(dev_priv->fb_heap, set->fb->mm_handle); +	fb_info.width = set->fb->width; +	fb_info.height = set->fb->height; +	fb_info.depth = set->fb->depth; +	fb_info.bpp = set->fb->bits_per_pixel; +	fb_info.x = set->x; +	fb_info.y = set->y; + +	rval = crtc->fb->bind(crtc, &fb_info); +	if (rval != 0) { +		NV50_DEBUG("fb_bind failed\n"); +		goto out; +	} + +	if (!crtc->cursor->enabled) { +		rval = crtc->cursor->enable(crtc); +		if (rval != 0) { +			NV50_DEBUG("cursor_enable failed\n"); +			goto out; +		} +	} + +	/* modeset time, finally */ + +	/* disconnect unused outputs */ +	list_for_each_entry(output, &display->outputs, head) { +		if (output->crtc) +			crtc_mask |= 1 << output->crtc->index; +		else +			output->execute_mode(output, TRUE); +	} + +	rval = crtc->set_mode(crtc, hw_mode); +	if (rval != 0) { +		NV50_DEBUG("crtc mode set failed\n"); +		goto out; +	} + +	/* find native mode. */ +	list_for_each_entry(output, &display->outputs, head) { +		if (output->crtc != crtc) +			continue; + +		*crtc->native_mode = *output->native_mode; +		list_for_each_entry(connector, &display->connectors, head) { +			if (connector->output != output) +				continue; + +			crtc->scaling_mode = connector->scaling_mode; +			break; +		} + +		if (crtc->scaling_mode == SCALE_PANEL) +			crtc->use_native_mode = false; +		else +			crtc->use_native_mode = true; + +		break; /* no use in finding more than one mode */ +	} + +	rval = crtc->execute_mode(crtc); +	if (rval != 0) { +		NV50_DEBUG("crtc execute mode failed\n"); +		goto out; +	} + +	list_for_each_entry(output, &display->outputs, head) { +		if (output->crtc != crtc) +			continue; + +		rval = output->execute_mode(output, FALSE); +		if (rval != 0) { +			NV50_DEBUG("output execute mode failed\n"); +			goto out; +		} +	} + +	rval = crtc->set_scale(crtc); +	if (rval != 0) { +		NV50_DEBUG("crtc set scale failed\n"); +		goto out; +	} + +	/* next line changes crtc, so putting it here is important */ +	display->last_crtc = crtc->index; + +	/* blank any unused crtcs */ +	list_for_each_entry(crtc, &display->crtcs, head) { +		if (!(crtc_mask & (1 << crtc->index))) +			crtc->blank(crtc, TRUE); +	} + +	display->update(display); + +	kfree(hw_mode); + +	return 0; + +out: +	display->update(display); + +	kfree(hw_mode); + +	if (rval != 0) +		return rval; +	else +		return -EINVAL; +} + +static void nv50_kms_crtc_destroy(struct drm_crtc *drm_crtc) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + +	drm_crtc_cleanup(drm_crtc); + +	/* this will even destroy the public structure. */ +	crtc->destroy(crtc); +} + +static const struct drm_crtc_funcs nv50_kms_crtc_funcs = { +	.save = NULL, +	.restore = NULL, +	.cursor_set = nv50_kms_crtc_cursor_set, +	.cursor_move = nv50_kms_crtc_cursor_move, +	.gamma_set = nv50_kms_crtc_gamma_set, +	.set_config = nv50_kms_crtc_set_config, +	.destroy = nv50_kms_crtc_destroy, +}; + +static int nv50_kms_crtcs_init(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_crtc *crtc = NULL; + +	/* +	 * This may look a bit confusing, but: +	 * The internal structure is already allocated and so is the public one. +	 * Just a matter of getting to the memory and register it. +	 */ +	list_for_each_entry(crtc, &display->crtcs, head) { +		struct drm_crtc *drm_crtc = to_nv50_kms_crtc(crtc); + +		drm_crtc_init(dev, drm_crtc, &nv50_kms_crtc_funcs); +	} + +	return 0; +} + +/* + * Encoder functions + */ + +static void nv50_kms_encoder_destroy(struct drm_encoder *drm_encoder) +{ +	struct nv50_output *output = to_nv50_output(drm_encoder); + +	drm_encoder_cleanup(drm_encoder); + +	/* this will even destroy the public structure. */ +	output->destroy(output); +} + +static const struct drm_encoder_funcs nv50_kms_encoder_funcs = { +	.destroy = nv50_kms_encoder_destroy, +}; + +static int nv50_kms_encoders_init(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_output *output = NULL; + +	list_for_each_entry(output, &display->outputs, head) { +		struct drm_encoder *drm_encoder = to_nv50_kms_encoder(output); +		uint32_t type = DRM_MODE_ENCODER_NONE; + +		switch (output->type) { +			case OUTPUT_DAC: +				type = DRM_MODE_ENCODER_DAC; +				break; +			case OUTPUT_TMDS: +				type = DRM_MODE_ENCODER_TMDS; +				break; +			case OUTPUT_LVDS: +				type = DRM_MODE_ENCODER_LVDS; +				break; +			case OUTPUT_TV: +				type = DRM_MODE_ENCODER_TVDAC; +				break; +			default: +				type = DRM_MODE_ENCODER_NONE; +				break; +		} + +		if (type == DRM_MODE_ENCODER_NONE) { +			DRM_ERROR("DRM_MODE_ENCODER_NONE encountered\n"); +			continue; +		} + +		drm_encoder_init(dev, drm_encoder, &nv50_kms_encoder_funcs, type); + +		/* I've never seen possible crtc's restricted. */ +		drm_encoder->possible_crtcs = 3; +		drm_encoder->possible_clones = 0; +	} + +	return 0; +} + +/* + * Connector functions + */ + +void nv50_kms_connector_detect_all(struct drm_device *dev) +{ +	struct drm_connector *drm_connector = NULL; +	enum drm_connector_status old, new; +	bool notify = false; + +	list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +		old = drm_connector->status; +		new = drm_connector->funcs->detect(drm_connector); + +		if (new != old) { +			notify = true; +			drm_connector->funcs->fill_modes(drm_connector, 0, 0); +		} +	} + +	/* I think this is the hook that notifies of changes. */ +	if (notify) +		dev->mode_config.funcs->fb_changed(dev); +} + +static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector) +{ +	struct nv50_connector *connector = to_nv50_connector(drm_connector); +	bool connected; + +	connected = connector->detect(connector); + +	if (connected) +		drm_connector->status = connector_status_connected; +	else +		drm_connector->status = connector_status_disconnected; + +	return drm_connector->status; +} + +static void nv50_kms_connector_destroy(struct drm_connector *drm_connector) +{ +	struct nv50_connector *connector = to_nv50_connector(drm_connector); + +	drm_sysfs_connector_remove(drm_connector); +	drm_connector_cleanup(drm_connector); + +	/* this will even destroy the public structure. */ +	connector->destroy(connector); +} + +/* + * Detailed mode info for a standard 640x480@60Hz monitor + */ +static struct drm_display_mode std_mode[] = { +	/*{ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656, +		 752, 800, 0, 480, 490, 492, 525, 0, +		 V_NHSYNC | V_NVSYNC) },*/ /* 640x480@60Hz */ +	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DEFAULT, 135000, 1280, 1296, +		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0, +		   V_PHSYNC | V_PVSYNC) }, /* 1280x1024@75Hz */ +}; + +static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, uint32_t maxX, uint32_t maxY) +{ +	struct nv50_connector *connector = to_nv50_connector(drm_connector); +	int ret = 0; +	bool connected; +	struct drm_display_mode *mode, *t; +	struct edid *edid = NULL; + +	DRM_DEBUG("%s\n", drm_get_connector_name(drm_connector)); +	/* set all modes to the unverified state */ +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) +		mode->status = MODE_UNVERIFIED; + +	connected = connector->detect(connector); + +	if (connected) +		drm_connector->status = connector_status_connected; +	else +		drm_connector->status = connector_status_disconnected; + +	if (!connected) { +		DRM_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector)); +		/* TODO set EDID to NULL */ +		return; +	} + +	/* Not all connnectors have an i2c channel. */ +	if (connector->i2c_chan) +		edid = (struct edid *) drm_do_probe_ddc_edid(&connector->i2c_chan->adapter); + +	if (edid) { +		drm_mode_connector_update_edid_property(drm_connector, edid); +		ret = drm_add_edid_modes(drm_connector, edid); +		connector->digital = edid->digital; /* cache */ +	} + +	if (ret) /* number of modes  > 1 */ +		drm_mode_connector_list_update(drm_connector); + +	if (maxX && maxY) +		drm_mode_validate_size(drm_connector->dev, &drm_connector->modes, maxX, maxY, 0); + +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { +		if (mode->status == MODE_OK) { +			struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); +			struct nv50_output *output = connector->to_output(connector, connector->digital); + +			mode->status = output->validate_mode(output, hw_mode); +			/* find native mode, TODO: also check if we actually found one */ +			if (mode->status == MODE_OK) { +				if (mode->type & DRM_MODE_TYPE_PREFERRED) +					*output->native_mode = *hw_mode; +			} +			kfree(hw_mode); +		} +	} + +	/* revalidate now that we have native mode */ +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { +		if (mode->status == MODE_OK) { +			struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); +			struct nv50_output *output = connector->to_output(connector, connector->digital); + +			mode->status = output->validate_mode(output, hw_mode); +			kfree(hw_mode); +		} +	} + +	drm_mode_prune_invalid(drm_connector->dev, &drm_connector->modes, TRUE); + +	if (list_empty(&drm_connector->modes)) { +		struct drm_display_mode *stdmode; +		struct nouveau_hw_mode *hw_mode; +		struct nv50_output *output; + +		DRM_DEBUG("No valid modes on %s\n", drm_get_connector_name(drm_connector)); + +		/* Should we do this here ??? +		 * When no valid EDID modes are available we end up +		 * here and bailed in the past, now we add a standard +		 * 640x480@60Hz mode and carry on. +		 */ +		stdmode = drm_mode_duplicate(drm_connector->dev, &std_mode[0]); +		drm_mode_probed_add(drm_connector, stdmode); +		drm_mode_list_concat(&drm_connector->probed_modes, +				     &drm_connector->modes); + +		/* also add it as native mode */ +		hw_mode = nv50_kms_to_hw_mode(mode); +		output = connector->to_output(connector, connector->digital); + +		if (hw_mode) +			*output->native_mode = *hw_mode; + +		DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", +			  drm_get_connector_name(drm_connector)); +	} + +	drm_mode_sort(&drm_connector->modes); + +	DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(drm_connector)); + +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { +		mode->vrefresh = drm_mode_vrefresh(mode); + +		/* is this needed, as it's unused by the driver? */ +		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); +		drm_mode_debug_printmodeline(mode); +	} +} + +static const struct drm_connector_funcs nv50_kms_connector_funcs = { +	.save = NULL, +	.restore = NULL, +	.detect = nv50_kms_connector_detect, +	.destroy = nv50_kms_connector_destroy, +	.fill_modes = nv50_kms_connector_fill_modes, +}; + +static int nv50_kms_connectors_init(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_connector *connector = NULL; +	int i; + +	list_for_each_entry(connector, &display->connectors, head) { +		struct drm_connector *drm_connector = to_nv50_kms_connector(connector); +		uint32_t type = DRM_MODE_CONNECTOR_Unknown; + +		switch (connector->type) { +			case CONNECTOR_VGA: +				type = DRM_MODE_CONNECTOR_VGA; +				break; +			case CONNECTOR_DVI_D: +				type = DRM_MODE_CONNECTOR_DVID; +				break; +			case CONNECTOR_DVI_I: +				type = DRM_MODE_CONNECTOR_DVII; +				break; +			case CONNECTOR_LVDS: +				type = DRM_MODE_CONNECTOR_LVDS; +				break; +			case CONNECTOR_TV: +				type = DRM_MODE_CONNECTOR_SVIDEO; +				break; +			default: +				type = DRM_MODE_CONNECTOR_Unknown; +				break; +		} + +		if (type == DRM_MODE_CONNECTOR_Unknown) { +			DRM_ERROR("DRM_MODE_CONNECTOR_Unknown encountered\n"); +			continue; +		} + +		/* It should be allowed sometimes, but let's be safe for the moment. */ +		drm_connector->interlace_allowed = false; +		drm_connector->doublescan_allowed = false; + +		drm_connector_init(dev, drm_connector, &nv50_kms_connector_funcs, type); + +		/* attach encoders, possibilities are analog + digital */ +		for (i = 0; i < 2; i++) { +			struct drm_encoder *drm_encoder = NULL; +			struct nv50_output *output = connector->to_output(connector, i); +			if (!output) +				continue; + +			drm_encoder = to_nv50_kms_encoder(output); +			if (!drm_encoder) { +				DRM_ERROR("No struct drm_connector to match struct nv50_output\n"); +				continue; +			} + +			drm_mode_connector_attach_encoder(drm_connector, drm_encoder); +		} + +		drm_sysfs_connector_add(drm_connector); +	} + +	return 0; +} + +/* + * Main functions + */ + +int nv50_kms_init(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_kms_priv *kms_priv = kzalloc(sizeof(struct nv50_kms_priv), GFP_KERNEL); +	struct nv50_display *display = NULL; +	int rval = 0; + +	dev_priv->kms_priv = kms_priv; + +	/* function pointers */ +	/* an allocation interface that deals with the outside world, without polluting the core. */ +	dev_priv->alloc_crtc = nv50_kms_alloc_crtc; +	dev_priv->alloc_output = nv50_kms_alloc_output; +	dev_priv->alloc_connector = nv50_kms_alloc_connector; + +	dev_priv->free_crtc = nv50_kms_free_crtc; +	dev_priv->free_output = nv50_kms_free_output; +	dev_priv->free_connector = nv50_kms_free_connector; + +	/* bios is needed for tables. */ +	rval = nouveau_parse_bios(dev); +	if (rval != 0) +		goto out; + +	/* init basic kernel modesetting */ +	drm_mode_config_init(dev); + +	dev->mode_config.min_width = 0; +	dev->mode_config.min_height = 0; + +	dev->mode_config.funcs = (void *)&nv50_kms_mode_funcs; + +	dev->mode_config.max_width = 8192; +	dev->mode_config.max_height = 8192; + +	dev->mode_config.fb_base = dev_priv->fb_phys; + +	/* init kms lists */ +	INIT_LIST_HEAD(&kms_priv->crtcs); +	INIT_LIST_HEAD(&kms_priv->encoders); +	INIT_LIST_HEAD(&kms_priv->connectors); + +	/* init the internal core, must be done first. */ +	rval = nv50_display_create(dev); +	if (rval != 0) +		goto out; + +	display = nv50_get_display(dev); +	if (!display) { +		rval = -EINVAL; +		goto out; +	} + +	/* pre-init now */ +	rval = display->pre_init(display); +	if (rval != 0) +		goto out; + +	/* init external layer */ +	rval = nv50_kms_crtcs_init(dev); +	if (rval != 0) +		goto out; + +	rval = nv50_kms_encoders_init(dev); +	if (rval != 0) +		goto out; + +	rval = nv50_kms_connectors_init(dev); +	if (rval != 0) +		goto out; + +	/* init now, this'll kill the textmode */ +	rval = display->init(display); +	if (rval != 0) +		goto out; + +	/* process cmdbuffer */ +	display->update(display); + +	return 0; + +out: +	kfree(kms_priv); +	dev_priv->kms_priv = NULL; + +	return rval; +} + +int nv50_kms_destroy(struct drm_device *dev) +{ +	drm_mode_config_cleanup(dev); + +	return 0; +} + diff --git a/linux-core/nv50_kms_wrapper.h b/linux-core/nv50_kms_wrapper.h new file mode 100644 index 00000000..3847d510 --- /dev/null +++ b/linux-core/nv50_kms_wrapper.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_KMS_WRAPPER_H__ +#define __NV50_KMS_WRAPPER_H__ + +#include "drmP.h" +#include "drm.h" + +#include "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_cursor.h" +#include "nv50_lut.h" +#include "nv50_fb.h" +#include "nv50_output.h" +#include "nv50_connector.h" + +#include "drm_crtc.h" +#include "drm_edid.h" + +/* Link internal modesetting structure to interface. */ + +struct nv50_kms_crtc { +	struct list_head head; + +	struct nv50_crtc priv; +	struct drm_crtc pub; +}; + +struct nv50_kms_encoder { +	struct list_head head; + +	struct nv50_output priv; +	struct drm_encoder pub; +}; + +struct nv50_kms_connector { +	struct list_head head; + +	struct nv50_connector priv; +	struct drm_connector pub; +}; + +struct nv50_kms_priv { +	struct list_head crtcs; +	struct list_head encoders; +	struct list_head connectors; +}; + +/* Get private functions. */ +#define from_nv50_kms_crtc(x) container_of(x, struct nv50_kms_crtc, pub) +#define from_nv50_crtc(x) container_of(x, struct nv50_kms_crtc, priv) +#define from_nv50_kms_encoder(x) container_of(x, struct nv50_kms_encoder, pub) +#define from_nv50_output(x) container_of(x, struct nv50_kms_encoder, priv) +#define from_nv50_kms_connector(x) container_of(x, struct nv50_kms_connector, pub) +#define from_nv50_connector(x) container_of(x, struct nv50_kms_connector, priv) + +#define to_nv50_crtc(x) (&(from_nv50_kms_crtc(x)->priv)) +#define to_nv50_kms_crtc(x) (&(from_nv50_crtc(x)->pub)) +#define to_nv50_output(x) (&(from_nv50_kms_encoder(x)->priv)) +#define to_nv50_kms_encoder(x) (&(from_nv50_output(x)->pub)) +#define to_nv50_connector(x) (&(from_nv50_kms_connector(x)->priv)) +#define to_nv50_kms_connector(x) (&(from_nv50_connector(x)->pub)) + +struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev); +void nv50_kms_connector_detect_all(struct drm_device *dev); + +int nv50_kms_init(struct drm_device *dev); +int nv50_kms_destroy(struct drm_device *dev); + +#endif /* __NV50_KMS_WRAPPER_H__ */ diff --git a/linux-core/nv50_lut.c b/linux-core/nv50_lut.c new file mode 100644 index 00000000..570900ba --- /dev/null +++ b/linux-core/nv50_lut.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_lut.h" +#include "nv50_fb.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_lut_alloc(struct nv50_crtc *crtc) +{ +	struct mem_block *block; +	struct drm_file *file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL); +	uint32_t flags = NOUVEAU_MEM_INTERNAL | NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED; + +	NV50_DEBUG("\n"); + +	/* Any file_priv should do as it's pointer is used as identification. */ +	block = nouveau_mem_alloc(crtc->dev, 0, 4096, flags, file_priv); + +	if (!block) +		return -ENOMEM; + +	crtc->lut->block = block; + +	return 0; +} + +static int nv50_lut_free(struct nv50_crtc *crtc) +{ +	struct drm_file *file_priv = crtc->lut->block->file_priv; + +	NV50_DEBUG("\n"); + +	nouveau_mem_free(crtc->dev, crtc->lut->block); + +	kfree(file_priv); + +	return 0; +} + +#define NV50_LUT_INDEX(val, w) ((val << (8 - w)) | (val >> ((w << 1) - 8))) +static int nv50_lut_set(struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue) +{ +	uint32_t index = 0, i; +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	void __iomem *lut = NULL; + +	NV50_DEBUG("\n"); + +	if (!crtc->lut || !crtc->lut->block) { +		DRM_ERROR("Something wrong with the LUT\n"); +		return -EINVAL; +	} + +	/* 16 bits, red, green, blue, unused, total of 64 bits per index */ +	/* maybe switch to ioremap_wc once it becomes available. */ +	lut = ioremap(dev_priv->fb_phys + crtc->lut->block->start, crtc->lut->block->size); +	if (!lut) { +		DRM_ERROR("ioremap failed on LUT\n"); +		return -EINVAL; +	} + +	/* 10 bits lut, with 14 bits values. */ +	switch (crtc->fb->depth) { +		case 15: +			/* R5G5B5 */ +			for (i = 0; i < 32; i++) { +				index = NV50_LUT_INDEX(i, 5); +				writew(red[i] >> 2, lut + 8*index + 0); +				writew(green[i] >> 2, lut + 8*index + 2); +				writew(blue[i] >> 2, lut + 8*index + 4); +			} +			break; +		case 16: +			/* R5G6B5 */ +			for (i = 0; i < 32; i++) { +				index = NV50_LUT_INDEX(i, 5); +				writew(red[i] >> 2, lut + 8*index + 0); +				writew(blue[i] >> 2, lut + 8*index + 4); +			} + +			/* Green has an extra bit. */ +			for (i = 0; i < 64; i++) { +				index = NV50_LUT_INDEX(i, 6); +				writew(green[i] >> 2, lut + 8*index + 2); +			} +			break; +		default: +			/* R8G8B8 */ +			for (i = 0; i < 256; i++) { +				writew(red[i] >> 2, lut + 8*i + 0); +				writew(green[i] >> 2, lut + 8*i + 2); +				writew(blue[i] >> 2, lut + 8*i + 4); +			} +			break; +	} + +	crtc->lut->depth = crtc->fb->depth; + +	/* Cleaning time. */ +	iounmap(lut); + +	return 0; +} + +int nv50_lut_create(struct nv50_crtc *crtc) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	crtc->lut = kzalloc(sizeof(struct nv50_lut), GFP_KERNEL); + +	rval = nv50_lut_alloc(crtc); +	if (rval != 0) +		return rval; + +	/* lut will be inited when fb is bound */ +	crtc->lut->depth = 0; + +	/* function pointers */ +	crtc->lut->set = nv50_lut_set; + +	return 0; +} + +int nv50_lut_destroy(struct nv50_crtc *crtc) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	rval = nv50_lut_free(crtc); + +	kfree(crtc->lut); +	crtc->lut = NULL; + +	if (rval != 0) +		return rval; + +	return 0; +} diff --git a/linux-core/nv50_lut.h b/linux-core/nv50_lut.h new file mode 100644 index 00000000..06704830 --- /dev/null +++ b/linux-core/nv50_lut.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_LUT_H__ +#define __NV50_LUT_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_lut { +	struct mem_block *block; + +	int depth; /* check against fb to see if it needs to be reuploaded */ + +	int (*set) (struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue); +}; + +int nv50_lut_create(struct nv50_crtc *crtc); +int nv50_lut_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_LUT_H__ */ diff --git a/linux-core/nv50_output.c b/linux-core/nv50_output.c new file mode 100644 index 00000000..de0017b9 --- /dev/null +++ b/linux-core/nv50_output.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_output.h" + +int nv50_output_or_offset(struct nv50_output *output) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	return ffs(dev_priv->dcb_table.entry[output->dcb_entry].or) - 1; +} diff --git a/linux-core/nv50_output.h b/linux-core/nv50_output.h new file mode 100644 index 00000000..bdee2826 --- /dev/null +++ b/linux-core/nv50_output.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV50_OUTPUT_H__ +#define __NV50_OUTPUT_H__ + +#include "nv50_crtc.h" + +#define OUTPUT_UNKNOWN 0 +#define OUTPUT_DAC 1 +#define OUTPUT_TMDS 2 +#define OUTPUT_LVDS 3 +#define OUTPUT_TV 4 + +struct nv50_output { +	struct list_head head; + +	struct drm_device *dev; +	int bus; +	int dcb_entry; +	int type; + +	struct nv50_crtc *crtc; +	struct nouveau_hw_mode *native_mode; + +	int (*validate_mode) (struct nv50_output *output, struct nouveau_hw_mode *mode); +	int (*execute_mode) (struct nv50_output *output, bool disconnect); +	int (*set_clock_mode) (struct nv50_output *output); +	bool (*detect) (struct nv50_output *output); +	int (*destroy) (struct nv50_output *output); +}; + +int nv50_output_or_offset(struct nv50_output *output); +int nv50_sor_create(struct drm_device *dev, int dcb_entry); +int nv50_dac_create(struct drm_device *dev, int dcb_entry); + +#endif /* __NV50_OUTPUT_H__ */ diff --git a/linux-core/nv50_sor.c b/linux-core/nv50_sor.c new file mode 100644 index 00000000..430c1e83 --- /dev/null +++ b/linux-core/nv50_sor.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "nv50_output.h" + +static int nv50_sor_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode) +{ +	NV50_DEBUG("\n"); + +	if (mode->clock > 165000) /* no dual link until we figure it out completely */ +		return MODE_CLOCK_HIGH; + +	if (mode->clock < 25000) +		return MODE_CLOCK_LOW; + +	if (output->native_mode->hdisplay > 0 && output->native_mode->vdisplay > 0) { +		if (mode->hdisplay > output->native_mode->hdisplay || mode->vdisplay > output->native_mode->vdisplay) +			return MODE_PANEL; +	} + +	return MODE_OK; +} + +static int nv50_sor_execute_mode(struct nv50_output *output, bool disconnect) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	struct nv50_crtc *crtc = output->crtc; +	struct nouveau_hw_mode *desired_mode = NULL; + +	uint32_t offset = nv50_output_or_offset(output) * 0x40; + +	uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	if (disconnect) { +		NV50_DEBUG("Disconnecting SOR\n"); +		OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl); +		return 0; +	} + +	desired_mode = (crtc->use_native_mode ? crtc->native_mode : crtc->mode); + +	if (output->type == OUTPUT_LVDS) { +		mode_ctl |= NV50_SOR_MODE_CTRL_LVDS; +	} else { +		mode_ctl |= NV50_SOR_MODE_CTRL_TMDS; +		if (desired_mode->clock > 165000) +			mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK; +	} + +	if (crtc->index == 1) +		mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1; +	else +		mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0; + +	if (desired_mode->flags & V_NHSYNC) +		mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC; + +	if (desired_mode->flags & V_NVSYNC) +		mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC; + +	/* TODO: DPMS ?????? */ + +	OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl); + +	return 0; +} + +static int nv50_sor_set_clock_mode(struct nv50_output *output) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	struct nv50_crtc *crtc = output->crtc; + +	uint32_t limit = 165000; +	struct nouveau_hw_mode *hw_mode; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	if (crtc->use_native_mode) +		hw_mode = crtc->native_mode; +	else +		hw_mode = crtc->mode; + +	/* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */ +	/* I presume it's some kind of clock setting, but what precisely i do not know. */ +	NV_WRITE(NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(nv50_output_or_offset(output)), 0x70000 | ((hw_mode->clock > limit) ? 0x101 : 0)); + +	return 0; +} + +static int nv50_sor_destroy(struct nv50_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !output) +		return -EINVAL; + +	list_del(&output->head); + +	kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); + +	return 0; +} + +int nv50_sor_create(struct drm_device *dev, int dcb_entry) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_output *output = NULL; +	struct nv50_display *display = NULL; +	struct dcb_entry *entry = NULL; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_output) +		output = dev_priv->alloc_output(dev); + +	if (!output) +		return -ENOMEM; + +	output->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) +		goto out; + +	entry = &dev_priv->dcb_table.entry[dcb_entry]; +	if (!entry) +		goto out; + +	switch (entry->type) { +		case DCB_OUTPUT_TMDS: +			output->type = OUTPUT_TMDS; +			DRM_INFO("Detected a TMDS output\n"); +			break; +		case DCB_OUTPUT_LVDS: +			output->type = OUTPUT_LVDS; +			DRM_INFO("Detected a LVDS output\n"); +			break; +		default: +			goto out; +	} + +	output->dcb_entry = dcb_entry; +	output->bus = entry->bus; + +	list_add_tail(&output->head, &display->outputs); + +	output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + +	/* Set function pointers. */ +	output->validate_mode = nv50_sor_validate_mode; +	output->execute_mode = nv50_sor_execute_mode; +	output->set_clock_mode = nv50_sor_set_clock_mode; +	output->detect = NULL; +	output->destroy = nv50_sor_destroy; + +	/* Some default state, unknown what it precisely means. */ +	if (output->type == OUTPUT_TMDS) { +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_00C(nv50_output_or_offset(output)), 0x03010700); +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_010(nv50_output_or_offset(output)), 0x0000152f); +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_014(nv50_output_or_offset(output)), 0x00000000); +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_018(nv50_output_or_offset(output)), 0x00245af8); +	} + +	return 0; + +out: +	if (output->native_mode) +		kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); +	return -EINVAL; +} diff --git a/shared-core/nouveau_dma.h b/shared-core/nouveau_dma.h index ce3c58cb..07652c2b 100644 --- a/shared-core/nouveau_dma.h +++ b/shared-core/nouveau_dma.h @@ -93,4 +93,9 @@ typedef enum {  	}                                                                      \  } while(0) +/* This should allow easy switching to a real fifo in the future. */ +#define OUT_MODE(mthd, val) do {				\ +	nv50_display_command(dev_priv, mthd, val); 		\ +} while(0) +  #endif diff --git a/shared-core/nouveau_drv.h b/shared-core/nouveau_drv.h index a51e552c..20aa6b86 100644 --- a/shared-core/nouveau_drv.h +++ b/shared-core/nouveau_drv.h @@ -41,6 +41,9 @@  #include "nouveau_drm.h"  #include "nouveau_reg.h" +#include "nouveau_bios.h" + +#define MAX_NUM_DCB_ENTRIES 16  struct mem_block {  	struct mem_block *next; @@ -310,6 +313,28 @@ struct drm_nouveau_private {  	struct nouveau_config config;  	struct list_head gpuobj_list; + +	void *display_priv; /* internal modesetting */ +	void *kms_priv; /* related to public interface */ + +	/* Hook these up to the "public interface" to accomodate a certain allocation style. */ +	/* This is to avoid polluting the internal interface. */ +	void *(*alloc_crtc) (struct drm_device *dev); +	void *(*alloc_output) (struct drm_device *dev); +	void *(*alloc_connector) (struct drm_device *dev); + +	void (*free_crtc) (void *crtc); +	void (*free_output) (void *output); +	void (*free_connector) (void *connector); + +	struct bios bios; + +	struct { +		int entries; +		struct dcb_entry entry[MAX_NUM_DCB_ENTRIES]; +		unsigned char i2c_read[MAX_NUM_DCB_ENTRIES]; +		unsigned char i2c_write[MAX_NUM_DCB_ENTRIES]; +	} dcb_table;  };  #define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do {         \ @@ -353,6 +378,7 @@ extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,  						 struct drm_file *);  extern void nouveau_mem_takedown(struct mem_block **heap);  extern void nouveau_mem_free_block(struct mem_block *); +extern struct mem_block* find_block_by_handle(struct mem_block *heap, drm_handle_t handle);  extern uint64_t nouveau_mem_fb_amount(struct drm_device *);  extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);  extern int  nouveau_ioctl_mem_alloc(struct drm_device *, void *data, diff --git a/shared-core/nouveau_irq.c b/shared-core/nouveau_irq.c index 2a3d8a0b..e68b755f 100644 --- a/shared-core/nouveau_irq.c +++ b/shared-core/nouveau_irq.c @@ -37,6 +37,11 @@  #include "nouveau_reg.h"  #include "nouveau_swmthd.h" +/* needed for interrupt based vpll changes */ +#include "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_output.h" +  void  nouveau_irq_preinstall(struct drm_device *dev)  { @@ -503,11 +508,82 @@ static void  nouveau_nv50_display_irq_handler(struct drm_device *dev)  {  	struct drm_nouveau_private *dev_priv = dev->dev_private; -	uint32_t val = NV_READ(NV50_DISPLAY_SUPERVISOR); +	uint32_t val = NV_READ(NV50_PDISPLAY_SUPERVISOR);  	DRM_INFO("NV50_DISPLAY_INTR - 0x%08X\n", val); -	NV_WRITE(NV50_DISPLAY_SUPERVISOR, val); +	/* vblank interrupts */ +	if (val & NV50_PDISPLAY_SUPERVISOR_CRTCn) { +		NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CRTCn); +		val &= ~NV50_PDISPLAY_SUPERVISOR_CRTCn; +	} + +	/* clock setting amongst other things. */ +	if (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) { +		uint32_t state = (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) >> NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT; + +		NV50_DEBUG("state %d\n", state); + +		/* Set pll */ +		if (state == 2) { +			struct nv50_display *display = nv50_get_display(dev); +			struct nv50_output *output = NULL; +			struct nv50_crtc *crtc = NULL; +			int crtc_index; + +			uint32_t unk30 = NV_READ(NV50_PDISPLAY_UNK30_CTRL); + +			for (crtc_index = 0; crtc_index < 2; crtc_index++) { +				bool clock_change = false; +				bool clock_ack = false; + +				if (crtc_index == 0 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0)) +					clock_change = true; + +				if (crtc_index == 1 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1)) +					clock_change = true; + +				if (clock_change) +					clock_ack = true; + +				if (display->last_crtc == crtc_index) +					clock_ack = true; + +				list_for_each_entry(crtc, &display->crtcs, head) { +					if (crtc->index == crtc_index) +						break; +				} + +				if (clock_change) +					crtc->set_clock(crtc); + +				NV50_DEBUG("index %d clock_change %d clock_ack %d\n", crtc_index, clock_change, clock_ack); + +				if (!clock_ack) +					continue; + +				crtc->set_clock_mode(crtc); + +				list_for_each_entry(output, &display->outputs, head) { +					if (!output->crtc) +						continue; + +					if (output->crtc == crtc) +						output->set_clock_mode(output); +				} +			} +		} + +		NV_WRITE(NV50_PDISPLAY_UNK30_CTRL, NV50_PDISPLAY_UNK30_CTRL_PENDING); +		NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK); + +		val &= ~NV50_PDISPLAY_SUPERVISOR_CLK_MASK; +	} + +	if (val) +		DRM_ERROR("unsupported NV50_DISPLAY_INTR - 0x%08X\n", val); + +	NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val);  }  static void diff --git a/shared-core/nouveau_mem.c b/shared-core/nouveau_mem.c index 2cf8807d..810eaf9e 100644 --- a/shared-core/nouveau_mem.c +++ b/shared-core/nouveau_mem.c @@ -108,6 +108,17 @@ static struct mem_block *find_block(struct mem_block *heap, uint64_t start)  	return NULL;  } +struct mem_block *find_block_by_handle(struct mem_block *heap, drm_handle_t handle) +{ +	struct mem_block *p; + +	list_for_each(p, heap) +		if (p->map_handle == handle) +			return p; + +	return NULL; +} +  void nouveau_mem_free_block(struct mem_block *p)  {  	p->file_priv = NULL; diff --git a/shared-core/nouveau_reg.h b/shared-core/nouveau_reg.h index 1ae0177c..8cf7f889 100644 --- a/shared-core/nouveau_reg.h +++ b/shared-core/nouveau_reg.h @@ -116,6 +116,9 @@  #define NV04_PBUS_PCI_NV_1                                 0x00001804  #define NV04_PBUS_PCI_NV_19                                0x0000184C +#define NV04_PBUS_PCI_NV_20				0x00001850 +#	define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED		(0 << 0) +#	define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED		(1 << 0)  #define NV04_PTIMER_INTR_0                                 0x00009100  #define NV04_PTIMER_INTR_EN_0                              0x00009140 @@ -542,6 +545,8 @@  /* This name is a partial guess. */  #define NV50_DISPLAY_SUPERVISOR                     0x00610024 +#define NV04_PRAMIN						0x00700000 +  /* Fifo commands. These are not regs, neither masks */  #define NV03_FIFO_CMD_JUMP                                 0x20000000  #define NV03_FIFO_CMD_JUMP_OFFSET_MASK                     0x1ffffffc @@ -591,3 +596,296 @@  #define NV40_RAMFC_UNK_48                                        0x48  #define NV40_RAMFC_UNK_4C                                        0x4C  #define NV40_RAMFC_UNK_50                                        0x50 + +/* This is a partial import from rules-ng, a few things may be duplicated. + * Eventually we should completely import everything from rules-ng. + * For the moment check rules-ng for docs. +  */ + +#define NV50_PMC                                            0x00000000 +#define NV50_PMC__LEN                                              0x1 +#define NV50_PMC__ESIZE                                         0x2000 +#    define NV50_PMC_BOOT_0                                 0x00000000 +#        define NV50_PMC_BOOT_0_REVISION                    0x000000ff +#        define NV50_PMC_BOOT_0_REVISION__SHIFT                      0 +#        define NV50_PMC_BOOT_0_ARCH                        0x0ff00000 +#        define NV50_PMC_BOOT_0_ARCH__SHIFT                         20 +#    define NV50_PMC_INTR_0                                 0x00000100 +#        define NV50_PMC_INTR_0_PFIFO                           (1<<8) +#        define NV50_PMC_INTR_0_PGRAPH                         (1<<12) +#        define NV50_PMC_INTR_0_PTIMER                         (1<<20) +#        define NV50_PMC_INTR_0_HOTPLUG                        (1<<21) +#        define NV50_PMC_INTR_0_DISPLAY                        (1<<26) +#    define NV50_PMC_INTR_EN_0                              0x00000140 +#        define NV50_PMC_INTR_EN_0_MASTER                       (1<<0) +#            define NV50_PMC_INTR_EN_0_MASTER_DISABLED          (0<<0) +#            define NV50_PMC_INTR_EN_0_MASTER_ENABLED           (1<<0) +#    define NV50_PMC_ENABLE                                 0x00000200 +#        define NV50_PMC_ENABLE_PFIFO                           (1<<8) +#        define NV50_PMC_ENABLE_PGRAPH                         (1<<12) + +#define NV50_PCONNECTOR                                     0x0000e000 +#define NV50_PCONNECTOR__LEN                                       0x1 +#define NV50_PCONNECTOR__ESIZE                                  0x1000 +#    define NV50_PCONNECTOR_HOTPLUG_INTR                    0x0000e050 +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0          (1<<0) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1          (1<<1) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2          (1<<2) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3          (1<<3) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C4          (1<<4) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C5          (1<<5) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C6          (1<<6) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C7          (1<<7) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C8          (1<<8) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C9          (1<<9) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C10        (1<<10) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C11        (1<<11) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C12        (1<<12) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C13        (1<<13) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C14        (1<<14) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C15        (1<<15) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0       (1<<16) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1       (1<<17) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2       (1<<18) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3       (1<<19) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C4       (1<<20) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C5       (1<<21) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C6       (1<<22) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C7       (1<<23) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C8       (1<<24) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C9       (1<<25) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C10      (1<<26) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C11      (1<<27) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C12      (1<<28) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C13      (1<<29) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C14      (1<<30) +#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C15      (1<<31) +#    define NV50_PCONNECTOR_HOTPLUG_CTRL                    0x0000e054 +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0          (1<<0) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1          (1<<1) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2          (1<<2) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3          (1<<3) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C4          (1<<4) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C5          (1<<5) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C6          (1<<6) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C7          (1<<7) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C8          (1<<8) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C9          (1<<9) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C10        (1<<10) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C11        (1<<11) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C12        (1<<12) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C13        (1<<13) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C14        (1<<14) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C15        (1<<15) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0       (1<<16) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1       (1<<17) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2       (1<<18) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3       (1<<19) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C4       (1<<20) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C5       (1<<21) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C6       (1<<22) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C7       (1<<23) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C8       (1<<24) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C9       (1<<25) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C10      (1<<26) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C11      (1<<27) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C12      (1<<28) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C13      (1<<29) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C14      (1<<30) +#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C15      (1<<31) +#    define NV50_PCONNECTOR_HOTPLUG_STATE1                  0x0000e104 +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C0 (1<<2) +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C1 (1<<6) +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C2 (1<<10) +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C3 (1<<14) +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C4 (1<<18) +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C5 (1<<22) +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C6 (1<<26) +#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C7 (1<<30) +#    define NV50_PCONNECTOR_HOTPLUG_STATE2                  0x0000e108 +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C8 (1<<2) +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C9 (1<<6) +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C10 (1<<10) +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C11 (1<<14) +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C12 (1<<18) +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C13 (1<<22) +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C14 (1<<26) +#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C15 (1<<30) +#    define NV50_PCONNECTOR_I2C                             0x0000e138 +#    define NV50_PCONNECTOR_I2C__LEN                              0x10 +#    define NV50_PCONNECTOR_I2C__ESIZE                            0x18 +#        define NV50_PCONNECTOR_I2C_PORT(i)      (0x0000e138+(i)*0x18) + + +#define NV50_PBUS                                           0x00088000 +#define NV50_PBUS__LEN                                             0x1 +#define NV50_PBUS__ESIZE                                        0x1000 +#    define NV50_PBUS_PCI_ID                                0x00088000 +#        define NV50_PBUS_PCI_ID_VENDOR_ID                  0x0000ffff +#        define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT                    0 +#        define NV50_PBUS_PCI_ID_DEVICE_ID                  0xffff0000 +#        define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT                   16 + +#define NV50_PFB                                            0x00100000 +#define NV50_PFB__LEN                                              0x1 +#define NV50_PFB__ESIZE                                         0x1000 + +#define NV50_PEXTDEV                                        0x00101000 +#define NV50_PEXTDEV__LEN                                          0x1 +#define NV50_PEXTDEV__ESIZE                                     0x1000 + +#define NV50_PROM                                           0x00300000 +#define NV50_PROM__LEN                                             0x1 +#define NV50_PROM__ESIZE                                       0x10000 + +#define NV50_PGRAPH                                         0x00400000 +#define NV50_PGRAPH__LEN                                           0x1 +#define NV50_PGRAPH__ESIZE                                     0x10000 + +#define NV50_PDISPLAY                                       0x00610000 +#define NV50_PDISPLAY__LEN                                         0x1 +#define NV50_PDISPLAY__ESIZE                                   0x10000 +#    define NV50_PDISPLAY_SUPERVISOR                        0x00610024 +#        define NV50_PDISPLAY_SUPERVISOR_CRTCn              0x0000000c +#        define NV50_PDISPLAY_SUPERVISOR_CRTCn__SHIFT                2 +#        define NV50_PDISPLAY_SUPERVISOR_CRTC0                  (1<<2) +#        define NV50_PDISPLAY_SUPERVISOR_CRTC1                  (1<<3) +#        define NV50_PDISPLAY_SUPERVISOR_CLK_MASK           0x00000070 +#        define NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT             4 +#        define NV50_PDISPLAY_SUPERVISOR_CLK_UPDATE             (1<<5) +#    define NV50_PDISPLAY_SUPERVISOR_INTR                   0x0061002c +#        define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC0      (1<<2) +#        define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC1      (1<<3) +#        define NV50_PDISPLAY_SUPERVISOR_INTR_UNK1              (1<<4) +#        define NV50_PDISPLAY_SUPERVISOR_INTR_CLK_UPDATE        (1<<5) +#        define NV50_PDISPLAY_SUPERVISOR_INTR_UNK4              (1<<6) +#    define NV50_PDISPLAY_UNK30_CTRL                        0x00610030 +#        define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0           (1<<9) +#        define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1          (1<<10) +#        define NV50_PDISPLAY_UNK30_CTRL_PENDING               (1<<31) +#    define NV50_PDISPLAY_UNK50_CTRL                        0x00610050 +#        define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE           (1<<1) +#        define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK  0x00000003 +#        define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK__SHIFT    0 +#        define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE           (1<<9) +#        define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK  0x00000300 +#        define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK__SHIFT    8 +#    define NV50_PDISPLAY_UNK200_CTRL                       0x00610200 +#    define NV50_PDISPLAY_CURSOR                            0x00610270 +#    define NV50_PDISPLAY_CURSOR__LEN                              0x2 +#    define NV50_PDISPLAY_CURSOR__ESIZE                           0x10 +#        define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) (0x00610270+(i)*0x10) +#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON        (1<<0) +#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK 0x00030000 +#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK__SHIFT 16 +#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE (1<<16) + +#    define NV50_PDISPLAY_CTRL_STATE                        0x00610300 +#        define NV50_PDISPLAY_CTRL_STATE_ENABLE                 (1<<0) +#        define NV50_PDISPLAY_CTRL_STATE_PENDING               (1<<31) +#    define NV50_PDISPLAY_CTRL_VAL                          0x00610304 +#    define NV50_PDISPLAY_UNK_380                           0x00610380 +#    define NV50_PDISPLAY_RAM_AMOUNT                        0x00610384 +#    define NV50_PDISPLAY_UNK_388                           0x00610388 +#    define NV50_PDISPLAY_UNK_38C                           0x0061038c +#    define NV50_PDISPLAY_CRTC_VAL                          0x00610a00 +#    define NV50_PDISPLAY_CRTC_VAL__LEN                            0x2 +#            define NV50_PDISPLAY_CRTC_VAL_UNK_900(i,j) (0x00610a18+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_CLUT_MODE(i,j) (0x00610a24+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_INTERLACE(i,j) (0x00610a48+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_SCALE_CTRL(i,j) (0x00610a50+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_CURSOR_CTRL(i,j) (0x00610a58+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_UNK_904(i,j) (0x00610ab8+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_DEPTH(i,j) (0x00610ac8+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_CLOCK(i,j) (0x00610ad0+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_COLOR_CTRL(i,j) (0x00610ae0+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_SYNC_START_TO_BLANK_END(i,j) (0x00610ae8+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_MODE_UNK1(i,j) (0x00610af0+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_DISPLAY_TOTAL(i,j) (0x00610af8+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_SYNC_DURATION(i,j) (0x00610b00+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_MODE_UNK2(i,j) (0x00610b08+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_UNK_828(i,j) (0x00610b10+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_FB_SIZE(i,j) (0x00610b18+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_FB_PITCH(i,j) (0x00610b20+(i)*0x540+(j)*0x4) +#                define NV50_PDISPLAY_CRTC_VAL_FB_PITCH_LINEAR_FB (1<<20) +#            define NV50_PDISPLAY_CRTC_VAL_FB_POS(i,j) (0x00610b28+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_SCALE_CENTER_OFFSET(i,j) (0x00610b38+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_REAL_RES(i,j) (0x00610b40+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_SCALE_RES1(i,j) (0x00610b48+(i)*0x540+(j)*0x4) +#            define NV50_PDISPLAY_CRTC_VAL_SCALE_RES2(i,j) (0x00610b50+(i)*0x540+(j)*0x4) + + +#            define NV50_PDISPLAY_DAC_VAL_MODE_CTRL(i,j) (0x00610b58+(i)*0x8+(j)*0x4) + + +#            define NV50_PDISPLAY_SOR_VAL_MODE_CTRL(i,j) (0x00610b70+(i)*0x8+(j)*0x4) + + +#            define NV50_PDISPLAY_DAC_VAL_MODE_CTRL2(i,j) (0x00610bdc+(i)*0x8+(j)*0x4) + + +#    define NV50_PDISPLAY_CRTC_CLK                          0x00614000 +#    define NV50_PDISPLAY_CRTC_CLK__LEN                            0x2 +#        define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(i) (0x00614100+(i)*0x800) +#            define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED 0x00000600 +#            define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED__SHIFT 9 +#        define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) (0x00614104+(i)*0x800) +#        define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) (0x00614108+(i)*0x800) +#        define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(i) (0x00614200+(i)*0x800) + +#    define NV50_PDISPLAY_DAC_CLK                           0x00614000 +#    define NV50_PDISPLAY_DAC_CLK__LEN                             0x3 +#        define NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(i) (0x00614280+(i)*0x800) + +#    define NV50_PDISPLAY_SOR_CLK                           0x00614000 +#    define NV50_PDISPLAY_SOR_CLK__LEN                             0x3 +#        define NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(i) (0x00614300+(i)*0x800) + +#    define NV50_PDISPLAY_DAC_REGS                          0x0061a000 +#    define NV50_PDISPLAY_DAC_REGS__LEN                            0x3 +#    define NV50_PDISPLAY_DAC_REGS__ESIZE                        0x800 +#        define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i) (0x0061a004+(i)*0x800) +#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF  (1<<0) +#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF  (1<<2) +#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED    (1<<4) +#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF        (1<<6) +#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING   (1<<31) +#        define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(i) (0x0061a00c+(i)*0x800) +#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE    (1<<20) +#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT 0x38000000 +#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT__SHIFT 29 +#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_DONE      (1<<31) +#        define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i) (0x0061a010+(i)*0x800) +#            define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED 0x00000600 +#            define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED__SHIFT 9 + +#    define NV50_PDISPLAY_SOR_REGS                          0x0061c000 +#    define NV50_PDISPLAY_SOR_REGS__LEN                            0x2 +#    define NV50_PDISPLAY_SOR_REGS__ESIZE                        0x800 +#        define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(i) (0x0061c004+(i)*0x800) +#            define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON         (1<<0) +#            define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING   (1<<31) +#        define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1(i) (0x0061c008+(i)*0x800) +#            define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED 0x00000600 +#            define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED__SHIFT 9 +#        define NV50_PDISPLAY_SOR_REGS_UNK_00C(i) (0x0061c00c+(i)*0x800) +#        define NV50_PDISPLAY_SOR_REGS_UNK_010(i) (0x0061c010+(i)*0x800) +#        define NV50_PDISPLAY_SOR_REGS_UNK_014(i) (0x0061c014+(i)*0x800) +#        define NV50_PDISPLAY_SOR_REGS_UNK_018(i) (0x0061c018+(i)*0x800) +#        define NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i) (0x0061c030+(i)*0x800) +#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE 0x00030000 +#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE__SHIFT 16 +#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_BLANKED  (1<<19) +#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT     (1<<28) + + +#define NV50_UNK640000                                      0x00640000 +#define NV50_UNK640000__LEN                                        0x6 +#define NV50_UNK640000__ESIZE                                   0x1000 +#    define NV50_UNK640000_UNK_000(i)          (0x00640000+(i)*0x1000) + +#define NV50_HW_CURSOR                                      0x00647000 +#define NV50_HW_CURSOR__LEN                                        0x2 +#define NV50_HW_CURSOR__ESIZE                                   0x1000 +#    define NV50_HW_CURSOR_POS_CTRL(i)              (0x00647080+(i)*0x1000) +#    define NV50_HW_CURSOR_POS(i)         (0x00647084+(i)*0x1000) diff --git a/shared-core/nouveau_state.c b/shared-core/nouveau_state.c index d9c6efe7..82591c64 100644 --- a/shared-core/nouveau_state.c +++ b/shared-core/nouveau_state.c @@ -27,6 +27,7 @@  #include "drm_sarea.h"  #include "nouveau_drv.h"  #include "nouveau_drm.h" +#include "nv50_kms_wrapper.h"  static int nouveau_init_card_mappings(struct drm_device *dev)  { @@ -362,6 +363,13 @@ nouveau_card_init(struct drm_device *dev)  	if (ret) return ret;  	dev_priv->init_state = NOUVEAU_CARD_INIT_DONE; + +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		if (dev_priv->card_type >= NV_50) { +			nv50_kms_init(dev); +			nv50_kms_connector_detect_all(dev); +		} +  	return 0;  } @@ -410,8 +418,7 @@ void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)  	nouveau_mem_release(file_priv,dev_priv->pci_heap);  } -/* first module load, setup the mmio/fb mapping */ -int nouveau_firstopen(struct drm_device *dev) +int nouveau_setup_mappings(struct drm_device *dev)  {  #if defined(__powerpc__)  	struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -457,6 +464,16 @@ int nouveau_firstopen(struct drm_device *dev)  	return 0;  } +/* first module load, setup the mmio/fb mapping */ +/* KMS: we need mmio at load time, not when the first drm client opens. */ +int nouveau_firstopen(struct drm_device *dev) +{ +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		return 0; + +	return nouveau_setup_mappings(dev); +} +  #define NV40_CHIPSET_MASK 0x00000baf  #define NV44_CHIPSET_MASK 0x00005450 @@ -549,10 +566,23 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)  	dev->dev_private = (void *)dev_priv; +	/* init card now, otherwise bad things happen */ +	if (drm_core_check_feature(dev, DRIVER_MODESET)) { +		int rval = 0; + +		rval = nouveau_setup_mappings(dev); +		if (rval != 0) +			return rval; + +		rval = nouveau_card_init(dev); +		if (rval != 0) +			return rval; +	} +  	return 0;  } -void nouveau_lastclose(struct drm_device *dev) +void nouveau_close(struct drm_device *dev)  {  	struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -568,8 +598,22 @@ void nouveau_lastclose(struct drm_device *dev)  	}  } +/* KMS: we need mmio at load time, not when the first drm client opens. */ +void nouveau_lastclose(struct drm_device *dev) +{ +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		return; + +	nouveau_close(dev); +} +  int nouveau_unload(struct drm_device *dev)  { +	if (drm_core_check_feature(dev, DRIVER_MODESET)) { +		nv50_kms_destroy(dev); +		nouveau_close(dev); +	} +  	drm_free(dev->dev_private, sizeof(*dev->dev_private), DRM_MEM_DRIVER);  	dev->dev_private = NULL;  	return 0; | 
