aboutsummaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
authorMaarten Maathuis <madman2003@gmail.com>2008-06-22 16:29:00 +0200
committerMaarten Maathuis <madman2003@gmail.com>2008-06-22 16:29:00 +0200
commit473a1997ace1a9fb545d0457549e50d17eb36175 (patch)
tree93ed8c730a8be1de2564088c4f5aaafc667a1263 /linux-core
parent7010d500072977f63a0bac08f2141d69dbd19595 (diff)
NV50: Initial import of kernel modesetting.
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/Makefile2
-rw-r--r--linux-core/Makefile.kernel5
-rw-r--r--linux-core/drm_crtc.c1
-rw-r--r--linux-core/drm_crtc.h1
-rw-r--r--linux-core/drm_edid.c34
-rw-r--r--linux-core/drm_modes.c5
-rw-r--r--linux-core/nouveau_bios.c815
-rw-r--r--linux-core/nouveau_bios.h151
-rw-r--r--linux-core/nouveau_drv.c7
-rw-r--r--linux-core/nv50_connector.c207
-rw-r--r--linux-core/nv50_connector.h60
-rw-r--r--linux-core/nv50_crtc.c515
-rw-r--r--linux-core/nv50_crtc.h67
-rw-r--r--linux-core/nv50_cursor.c177
-rw-r--r--linux-core/nv50_cursor.h51
-rw-r--r--linux-core/nv50_dac.c177
-rw-r--r--linux-core/nv50_display.c337
-rw-r--r--linux-core/nv50_display.h83
-rw-r--r--linux-core/nv50_display_commands.h196
-rw-r--r--linux-core/nv50_fb.c136
-rw-r--r--linux-core/nv50_fb.h55
-rw-r--r--linux-core/nv50_i2c.c356
-rw-r--r--linux-core/nv50_i2c.h47
-rw-r--r--linux-core/nv50_kms_wrapper.c960
-rw-r--r--linux-core/nv50_kms_wrapper.h94
-rw-r--r--linux-core/nv50_lut.c171
-rw-r--r--linux-core/nv50_lut.h45
-rw-r--r--linux-core/nv50_output.c33
-rw-r--r--linux-core/nv50_output.h60
-rw-r--r--linux-core/nv50_sor.c204
30 files changed, 5038 insertions, 14 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;
+}