NV50: Initial import of kernel modesetting.
authorMaarten Maathuis <madman2003@gmail.com>
Sun, 22 Jun 2008 14:29:00 +0000 (16:29 +0200)
committerMaarten Maathuis <madman2003@gmail.com>
Sun, 22 Jun 2008 14:29:00 +0000 (16:29 +0200)
36 files changed:
linux-core/Makefile
linux-core/Makefile.kernel
linux-core/drm_crtc.c
linux-core/drm_crtc.h
linux-core/drm_edid.c
linux-core/drm_modes.c
linux-core/nouveau_bios.c [new file with mode: 0644]
linux-core/nouveau_bios.h [new file with mode: 0644]
linux-core/nouveau_drv.c
linux-core/nv50_connector.c [new file with mode: 0644]
linux-core/nv50_connector.h [new file with mode: 0644]
linux-core/nv50_crtc.c [new file with mode: 0644]
linux-core/nv50_crtc.h [new file with mode: 0644]
linux-core/nv50_cursor.c [new file with mode: 0644]
linux-core/nv50_cursor.h [new file with mode: 0644]
linux-core/nv50_dac.c [new file with mode: 0644]
linux-core/nv50_display.c [new file with mode: 0644]
linux-core/nv50_display.h [new file with mode: 0644]
linux-core/nv50_display_commands.h [new file with mode: 0644]
linux-core/nv50_fb.c [new file with mode: 0644]
linux-core/nv50_fb.h [new file with mode: 0644]
linux-core/nv50_i2c.c [new file with mode: 0644]
linux-core/nv50_i2c.h [new file with mode: 0644]
linux-core/nv50_kms_wrapper.c [new file with mode: 0644]
linux-core/nv50_kms_wrapper.h [new file with mode: 0644]
linux-core/nv50_lut.c [new file with mode: 0644]
linux-core/nv50_lut.h [new file with mode: 0644]
linux-core/nv50_output.c [new file with mode: 0644]
linux-core/nv50_output.h [new file with mode: 0644]
linux-core/nv50_sor.c [new file with mode: 0644]
shared-core/nouveau_dma.h
shared-core/nouveau_drv.h
shared-core/nouveau_irq.c
shared-core/nouveau_mem.c
shared-core/nouveau_reg.h
shared-core/nouveau_state.c

index b9405bb..9b28885 100644 (file)
@@ -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) 
 
index ac9baf0..2343014 100644 (file)
@@ -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 \
index e1b371c..c8cfaef 100644 (file)
@@ -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)
 {
index 2b577b9..d6fa4cc 100644 (file)
@@ -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);
index 0d60039..812e64a 100644 (file)
@@ -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;
 }
 
index 3ed427f..df670c7 100644 (file)
@@ -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 (file)
index 0000000..8364741
--- /dev/null
@@ -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 (file)
index 0000000..e33ecd0
--- /dev/null
@@ -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__ */
index c8f57df..04f002f 100644 (file)
@@ -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 (file)
index 0000000..d13622b
--- /dev/null
@@ -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 (file)
index 0000000..c70d6ef
--- /dev/null
@@ -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 (file)
index 0000000..974f520
--- /dev/null
@@ -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 (file)
index 0000000..5eb815a
--- /dev/null
@@ -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 (file)
index 0000000..3d35b93
--- /dev/null
@@ -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 (file)
index 0000000..a2e4632
--- /dev/null
@@ -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 (file)
index 0000000..f827fed
--- /dev/null
@@ -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 (file)
index 0000000..56ddeb9
--- /dev/null
@@ -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 (file)
index 0000000..f20e67d
--- /dev/null
@@ -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 (file)
index 0000000..97b3d3c
--- /dev/null
@@ -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 (file)
index 0000000..b44b48a
--- /dev/null
@@ -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 (file)
index 0000000..6b28631
--- /dev/null
@@ -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 (file)
index 0000000..cf55645
--- /dev/null
@@ -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 (file)
index 0000000..1740f8e
--- /dev/null
@@ -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 (file)
index 0000000..01d4bc9
--- /dev/null
@@ -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 (file)
index 0000000..3847d51
--- /dev/null
@@ -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 (file)
index 0000000..570900b
--- /dev/null
@@ -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 (file)
index 0000000..0670483
--- /dev/null
@@ -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 (file)
index 0000000..de0017b
--- /dev/null
@@ -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 (file)
index 0000000..bdee282
--- /dev/null
@@ -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 (file)
index 0000000..430c1e8
--- /dev/null
@@ -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;
+}
index ce3c58c..07652c2 100644 (file)
@@ -93,4 +93,9 @@ typedef enum {
        }                                                                      \
 } while(0)
 
+/* This should allow easy switching to a real fifo in the future. */
+#define OUT_MODE(mthd, val) do {                               \
+       nv50_display_command(dev_priv, mthd, val);              \
+} while(0)
+
 #endif
index a51e552..20aa6b8 100644 (file)
@@ -41,6 +41,9 @@
 
 #include "nouveau_drm.h"
 #include "nouveau_reg.h"
+#include "nouveau_bios.h"
+
+#define MAX_NUM_DCB_ENTRIES 16
 
 struct mem_block {
        struct mem_block *next;
@@ -310,6 +313,28 @@ struct drm_nouveau_private {
        struct nouveau_config config;
 
        struct list_head gpuobj_list;
+
+       void *display_priv; /* internal modesetting */
+       void *kms_priv; /* related to public interface */
+
+       /* Hook these up to the "public interface" to accomodate a certain allocation style. */
+       /* This is to avoid polluting the internal interface. */
+       void *(*alloc_crtc) (struct drm_device *dev);
+       void *(*alloc_output) (struct drm_device *dev);
+       void *(*alloc_connector) (struct drm_device *dev);
+
+       void (*free_crtc) (void *crtc);
+       void (*free_output) (void *output);
+       void (*free_connector) (void *connector);
+
+       struct bios bios;
+
+       struct {
+               int entries;
+               struct dcb_entry entry[MAX_NUM_DCB_ENTRIES];
+               unsigned char i2c_read[MAX_NUM_DCB_ENTRIES];
+               unsigned char i2c_write[MAX_NUM_DCB_ENTRIES];
+       } dcb_table;
 };
 
 #define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do {         \
@@ -353,6 +378,7 @@ extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,
                                                 struct drm_file *);
 extern void nouveau_mem_takedown(struct mem_block **heap);
 extern void nouveau_mem_free_block(struct mem_block *);
+extern struct mem_block* find_block_by_handle(struct mem_block *heap, drm_handle_t handle);
 extern uint64_t nouveau_mem_fb_amount(struct drm_device *);
 extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);
 extern int  nouveau_ioctl_mem_alloc(struct drm_device *, void *data,
index 2a3d8a0..e68b755 100644 (file)
 #include "nouveau_reg.h"
 #include "nouveau_swmthd.h"
 
+/* needed for interrupt based vpll changes */
+#include "nv50_display.h"
+#include "nv50_crtc.h"
+#include "nv50_output.h"
+
 void
 nouveau_irq_preinstall(struct drm_device *dev)
 {
@@ -503,11 +508,82 @@ static void
 nouveau_nv50_display_irq_handler(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       uint32_t val = NV_READ(NV50_DISPLAY_SUPERVISOR);
+       uint32_t val = NV_READ(NV50_PDISPLAY_SUPERVISOR);
 
        DRM_INFO("NV50_DISPLAY_INTR - 0x%08X\n", val);
 
-       NV_WRITE(NV50_DISPLAY_SUPERVISOR, val);
+       /* vblank interrupts */
+       if (val & NV50_PDISPLAY_SUPERVISOR_CRTCn) {
+               NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CRTCn);
+               val &= ~NV50_PDISPLAY_SUPERVISOR_CRTCn;
+       }
+
+       /* clock setting amongst other things. */
+       if (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) {
+               uint32_t state = (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) >> NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT;
+
+               NV50_DEBUG("state %d\n", state);
+
+               /* Set pll */
+               if (state == 2) {
+                       struct nv50_display *display = nv50_get_display(dev);
+                       struct nv50_output *output = NULL;
+                       struct nv50_crtc *crtc = NULL;
+                       int crtc_index;
+
+                       uint32_t unk30 = NV_READ(NV50_PDISPLAY_UNK30_CTRL);
+
+                       for (crtc_index = 0; crtc_index < 2; crtc_index++) {
+                               bool clock_change = false;
+                               bool clock_ack = false;
+
+                               if (crtc_index == 0 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0))
+                                       clock_change = true;
+
+                               if (crtc_index == 1 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1))
+                                       clock_change = true;
+
+                               if (clock_change)
+                                       clock_ack = true;
+
+                               if (display->last_crtc == crtc_index)
+                                       clock_ack = true;
+
+                               list_for_each_entry(crtc, &display->crtcs, head) {
+                                       if (crtc->index == crtc_index)
+                                               break;
+                               }
+
+                               if (clock_change)
+                                       crtc->set_clock(crtc);
+
+                               NV50_DEBUG("index %d clock_change %d clock_ack %d\n", crtc_index, clock_change, clock_ack);
+
+                               if (!clock_ack)
+                                       continue;
+
+                               crtc->set_clock_mode(crtc);
+
+                               list_for_each_entry(output, &display->outputs, head) {
+                                       if (!output->crtc)
+                                               continue;
+
+                                       if (output->crtc == crtc)
+                                               output->set_clock_mode(output);
+                               }
+                       }
+               }
+
+               NV_WRITE(NV50_PDISPLAY_UNK30_CTRL, NV50_PDISPLAY_UNK30_CTRL_PENDING);
+               NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK);
+
+               val &= ~NV50_PDISPLAY_SUPERVISOR_CLK_MASK;
+       }
+
+       if (val)
+               DRM_ERROR("unsupported NV50_DISPLAY_INTR - 0x%08X\n", val);
+
+       NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val);
 }
 
 static void
index 2cf8807..810eaf9 100644 (file)
@@ -108,6 +108,17 @@ static struct mem_block *find_block(struct mem_block *heap, uint64_t start)
        return NULL;
 }
 
+struct mem_block *find_block_by_handle(struct mem_block *heap, drm_handle_t handle)
+{
+       struct mem_block *p;
+
+       list_for_each(p, heap)
+               if (p->map_handle == handle)
+                       return p;
+
+       return NULL;
+}
+
 void nouveau_mem_free_block(struct mem_block *p)
 {
        p->file_priv = NULL;
index 1ae0177..8cf7f88 100644 (file)
 
 #define NV04_PBUS_PCI_NV_1                                 0x00001804
 #define NV04_PBUS_PCI_NV_19                                0x0000184C
+#define NV04_PBUS_PCI_NV_20                            0x00001850
+#      define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED          (0 << 0)
+#      define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED           (1 << 0)
 
 #define NV04_PTIMER_INTR_0                                 0x00009100
 #define NV04_PTIMER_INTR_EN_0                              0x00009140
 /* This name is a partial guess. */
 #define NV50_DISPLAY_SUPERVISOR                     0x00610024
 
+#define NV04_PRAMIN                                            0x00700000
+
 /* Fifo commands. These are not regs, neither masks */
 #define NV03_FIFO_CMD_JUMP                                 0x20000000
 #define NV03_FIFO_CMD_JUMP_OFFSET_MASK                     0x1ffffffc
 #define NV40_RAMFC_UNK_48                                        0x48
 #define NV40_RAMFC_UNK_4C                                        0x4C
 #define NV40_RAMFC_UNK_50                                        0x50
+
+/* This is a partial import from rules-ng, a few things may be duplicated.
+ * Eventually we should completely import everything from rules-ng.
+ * For the moment check rules-ng for docs.
+  */
+
+#define NV50_PMC                                            0x00000000
+#define NV50_PMC__LEN                                              0x1
+#define NV50_PMC__ESIZE                                         0x2000
+#    define NV50_PMC_BOOT_0                                 0x00000000
+#        define NV50_PMC_BOOT_0_REVISION                    0x000000ff
+#        define NV50_PMC_BOOT_0_REVISION__SHIFT                      0
+#        define NV50_PMC_BOOT_0_ARCH                        0x0ff00000
+#        define NV50_PMC_BOOT_0_ARCH__SHIFT                         20
+#    define NV50_PMC_INTR_0                                 0x00000100
+#        define NV50_PMC_INTR_0_PFIFO                           (1<<8)
+#        define NV50_PMC_INTR_0_PGRAPH                         (1<<12)
+#        define NV50_PMC_INTR_0_PTIMER                         (1<<20)
+#        define NV50_PMC_INTR_0_HOTPLUG                        (1<<21)
+#        define NV50_PMC_INTR_0_DISPLAY                        (1<<26)
+#    define NV50_PMC_INTR_EN_0                              0x00000140
+#        define NV50_PMC_INTR_EN_0_MASTER                       (1<<0)
+#            define NV50_PMC_INTR_EN_0_MASTER_DISABLED          (0<<0)
+#            define NV50_PMC_INTR_EN_0_MASTER_ENABLED           (1<<0)
+#    define NV50_PMC_ENABLE                                 0x00000200
+#        define NV50_PMC_ENABLE_PFIFO                           (1<<8)
+#        define NV50_PMC_ENABLE_PGRAPH                         (1<<12)
+
+#define NV50_PCONNECTOR                                     0x0000e000
+#define NV50_PCONNECTOR__LEN                                       0x1
+#define NV50_PCONNECTOR__ESIZE                                  0x1000
+#    define NV50_PCONNECTOR_HOTPLUG_INTR                    0x0000e050
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0          (1<<0)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1          (1<<1)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2          (1<<2)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3          (1<<3)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C4          (1<<4)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C5          (1<<5)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C6          (1<<6)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C7          (1<<7)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C8          (1<<8)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C9          (1<<9)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C10        (1<<10)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C11        (1<<11)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C12        (1<<12)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C13        (1<<13)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C14        (1<<14)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C15        (1<<15)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0       (1<<16)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1       (1<<17)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2       (1<<18)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3       (1<<19)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C4       (1<<20)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C5       (1<<21)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C6       (1<<22)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C7       (1<<23)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C8       (1<<24)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C9       (1<<25)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C10      (1<<26)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C11      (1<<27)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C12      (1<<28)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C13      (1<<29)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C14      (1<<30)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C15      (1<<31)
+#    define NV50_PCONNECTOR_HOTPLUG_CTRL                    0x0000e054
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0          (1<<0)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1          (1<<1)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2          (1<<2)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3          (1<<3)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C4          (1<<4)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C5          (1<<5)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C6          (1<<6)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C7          (1<<7)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C8          (1<<8)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C9          (1<<9)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C10        (1<<10)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C11        (1<<11)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C12        (1<<12)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C13        (1<<13)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C14        (1<<14)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C15        (1<<15)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0       (1<<16)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1       (1<<17)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2       (1<<18)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3       (1<<19)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C4       (1<<20)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C5       (1<<21)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C6       (1<<22)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C7       (1<<23)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C8       (1<<24)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C9       (1<<25)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C10      (1<<26)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C11      (1<<27)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C12      (1<<28)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C13      (1<<29)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C14      (1<<30)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C15      (1<<31)
+#    define NV50_PCONNECTOR_HOTPLUG_STATE1                  0x0000e104
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C0 (1<<2)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C1 (1<<6)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C2 (1<<10)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C3 (1<<14)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C4 (1<<18)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C5 (1<<22)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C6 (1<<26)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C7 (1<<30)
+#    define NV50_PCONNECTOR_HOTPLUG_STATE2                  0x0000e108
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C8 (1<<2)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C9 (1<<6)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C10 (1<<10)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C11 (1<<14)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C12 (1<<18)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C13 (1<<22)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C14 (1<<26)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C15 (1<<30)
+#    define NV50_PCONNECTOR_I2C                             0x0000e138
+#    define NV50_PCONNECTOR_I2C__LEN                              0x10
+#    define NV50_PCONNECTOR_I2C__ESIZE                            0x18
+#        define NV50_PCONNECTOR_I2C_PORT(i)      (0x0000e138+(i)*0x18)
+
+
+#define NV50_PBUS                                           0x00088000
+#define NV50_PBUS__LEN                                             0x1
+#define NV50_PBUS__ESIZE                                        0x1000
+#    define NV50_PBUS_PCI_ID                                0x00088000
+#        define NV50_PBUS_PCI_ID_VENDOR_ID                  0x0000ffff
+#        define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT                    0
+#        define NV50_PBUS_PCI_ID_DEVICE_ID                  0xffff0000
+#        define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT                   16
+
+#define NV50_PFB                                            0x00100000
+#define NV50_PFB__LEN                                              0x1
+#define NV50_PFB__ESIZE                                         0x1000
+
+#define NV50_PEXTDEV                                        0x00101000
+#define NV50_PEXTDEV__LEN                                          0x1
+#define NV50_PEXTDEV__ESIZE                                     0x1000
+
+#define NV50_PROM                                           0x00300000
+#define NV50_PROM__LEN                                             0x1
+#define NV50_PROM__ESIZE                                       0x10000
+
+#define NV50_PGRAPH                                         0x00400000
+#define NV50_PGRAPH__LEN                                           0x1
+#define NV50_PGRAPH__ESIZE                                     0x10000
+
+#define NV50_PDISPLAY                                       0x00610000
+#define NV50_PDISPLAY__LEN                                         0x1
+#define NV50_PDISPLAY__ESIZE                                   0x10000
+#    define NV50_PDISPLAY_SUPERVISOR                        0x00610024
+#        define NV50_PDISPLAY_SUPERVISOR_CRTCn              0x0000000c
+#        define NV50_PDISPLAY_SUPERVISOR_CRTCn__SHIFT                2
+#        define NV50_PDISPLAY_SUPERVISOR_CRTC0                  (1<<2)
+#        define NV50_PDISPLAY_SUPERVISOR_CRTC1                  (1<<3)
+#        define NV50_PDISPLAY_SUPERVISOR_CLK_MASK           0x00000070
+#        define NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT             4
+#        define NV50_PDISPLAY_SUPERVISOR_CLK_UPDATE             (1<<5)
+#    define NV50_PDISPLAY_SUPERVISOR_INTR                   0x0061002c
+#        define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC0      (1<<2)
+#        define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC1      (1<<3)
+#        define NV50_PDISPLAY_SUPERVISOR_INTR_UNK1              (1<<4)
+#        define NV50_PDISPLAY_SUPERVISOR_INTR_CLK_UPDATE        (1<<5)
+#        define NV50_PDISPLAY_SUPERVISOR_INTR_UNK4              (1<<6)
+#    define NV50_PDISPLAY_UNK30_CTRL                        0x00610030
+#        define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0           (1<<9)
+#        define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1          (1<<10)
+#        define NV50_PDISPLAY_UNK30_CTRL_PENDING               (1<<31)
+#    define NV50_PDISPLAY_UNK50_CTRL                        0x00610050
+#        define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE           (1<<1)
+#        define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK  0x00000003
+#        define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK__SHIFT    0
+#        define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE           (1<<9)
+#        define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK  0x00000300
+#        define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK__SHIFT    8
+#    define NV50_PDISPLAY_UNK200_CTRL                       0x00610200
+#    define NV50_PDISPLAY_CURSOR                            0x00610270
+#    define NV50_PDISPLAY_CURSOR__LEN                              0x2
+#    define NV50_PDISPLAY_CURSOR__ESIZE                           0x10
+#        define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) (0x00610270+(i)*0x10)
+#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON        (1<<0)
+#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK 0x00030000
+#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK__SHIFT 16
+#            define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE (1<<16)
+
+#    define NV50_PDISPLAY_CTRL_STATE                        0x00610300
+#        define NV50_PDISPLAY_CTRL_STATE_ENABLE                 (1<<0)
+#        define NV50_PDISPLAY_CTRL_STATE_PENDING               (1<<31)
+#    define NV50_PDISPLAY_CTRL_VAL                          0x00610304
+#    define NV50_PDISPLAY_UNK_380                           0x00610380
+#    define NV50_PDISPLAY_RAM_AMOUNT                        0x00610384
+#    define NV50_PDISPLAY_UNK_388                           0x00610388
+#    define NV50_PDISPLAY_UNK_38C                           0x0061038c
+#    define NV50_PDISPLAY_CRTC_VAL                          0x00610a00
+#    define NV50_PDISPLAY_CRTC_VAL__LEN                            0x2
+#            define NV50_PDISPLAY_CRTC_VAL_UNK_900(i,j) (0x00610a18+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_CLUT_MODE(i,j) (0x00610a24+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_INTERLACE(i,j) (0x00610a48+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_SCALE_CTRL(i,j) (0x00610a50+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_CURSOR_CTRL(i,j) (0x00610a58+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_UNK_904(i,j) (0x00610ab8+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_DEPTH(i,j) (0x00610ac8+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_CLOCK(i,j) (0x00610ad0+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_COLOR_CTRL(i,j) (0x00610ae0+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_SYNC_START_TO_BLANK_END(i,j) (0x00610ae8+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_MODE_UNK1(i,j) (0x00610af0+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_DISPLAY_TOTAL(i,j) (0x00610af8+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_SYNC_DURATION(i,j) (0x00610b00+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_MODE_UNK2(i,j) (0x00610b08+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_UNK_828(i,j) (0x00610b10+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_FB_SIZE(i,j) (0x00610b18+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_FB_PITCH(i,j) (0x00610b20+(i)*0x540+(j)*0x4)
+#                define NV50_PDISPLAY_CRTC_VAL_FB_PITCH_LINEAR_FB (1<<20)
+#            define NV50_PDISPLAY_CRTC_VAL_FB_POS(i,j) (0x00610b28+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_SCALE_CENTER_OFFSET(i,j) (0x00610b38+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_REAL_RES(i,j) (0x00610b40+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_SCALE_RES1(i,j) (0x00610b48+(i)*0x540+(j)*0x4)
+#            define NV50_PDISPLAY_CRTC_VAL_SCALE_RES2(i,j) (0x00610b50+(i)*0x540+(j)*0x4)
+
+
+#            define NV50_PDISPLAY_DAC_VAL_MODE_CTRL(i,j) (0x00610b58+(i)*0x8+(j)*0x4)
+
+
+#            define NV50_PDISPLAY_SOR_VAL_MODE_CTRL(i,j) (0x00610b70+(i)*0x8+(j)*0x4)
+
+
+#            define NV50_PDISPLAY_DAC_VAL_MODE_CTRL2(i,j) (0x00610bdc+(i)*0x8+(j)*0x4)
+
+
+#    define NV50_PDISPLAY_CRTC_CLK                          0x00614000
+#    define NV50_PDISPLAY_CRTC_CLK__LEN                            0x2
+#        define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(i) (0x00614100+(i)*0x800)
+#            define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED 0x00000600
+#            define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED__SHIFT 9
+#        define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) (0x00614104+(i)*0x800)
+#        define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) (0x00614108+(i)*0x800)
+#        define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(i) (0x00614200+(i)*0x800)
+
+#    define NV50_PDISPLAY_DAC_CLK                           0x00614000
+#    define NV50_PDISPLAY_DAC_CLK__LEN                             0x3
+#        define NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(i) (0x00614280+(i)*0x800)
+
+#    define NV50_PDISPLAY_SOR_CLK                           0x00614000
+#    define NV50_PDISPLAY_SOR_CLK__LEN                             0x3
+#        define NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(i) (0x00614300+(i)*0x800)
+
+#    define NV50_PDISPLAY_DAC_REGS                          0x0061a000
+#    define NV50_PDISPLAY_DAC_REGS__LEN                            0x3
+#    define NV50_PDISPLAY_DAC_REGS__ESIZE                        0x800
+#        define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i) (0x0061a004+(i)*0x800)
+#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF  (1<<0)
+#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF  (1<<2)
+#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED    (1<<4)
+#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF        (1<<6)
+#            define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING   (1<<31)
+#        define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(i) (0x0061a00c+(i)*0x800)
+#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE    (1<<20)
+#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT 0x38000000
+#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT__SHIFT 29
+#            define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_DONE      (1<<31)
+#        define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i) (0x0061a010+(i)*0x800)
+#            define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED 0x00000600
+#            define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED__SHIFT 9
+
+#    define NV50_PDISPLAY_SOR_REGS                          0x0061c000
+#    define NV50_PDISPLAY_SOR_REGS__LEN                            0x2
+#    define NV50_PDISPLAY_SOR_REGS__ESIZE                        0x800
+#        define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(i) (0x0061c004+(i)*0x800)
+#            define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON         (1<<0)
+#            define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING   (1<<31)
+#        define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1(i) (0x0061c008+(i)*0x800)
+#            define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED 0x00000600
+#            define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED__SHIFT 9
+#        define NV50_PDISPLAY_SOR_REGS_UNK_00C(i) (0x0061c00c+(i)*0x800)
+#        define NV50_PDISPLAY_SOR_REGS_UNK_010(i) (0x0061c010+(i)*0x800)
+#        define NV50_PDISPLAY_SOR_REGS_UNK_014(i) (0x0061c014+(i)*0x800)
+#        define NV50_PDISPLAY_SOR_REGS_UNK_018(i) (0x0061c018+(i)*0x800)
+#        define NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i) (0x0061c030+(i)*0x800)
+#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE 0x00030000
+#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE__SHIFT 16
+#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_BLANKED  (1<<19)
+#            define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT     (1<<28)
+
+
+#define NV50_UNK640000                                      0x00640000
+#define NV50_UNK640000__LEN                                        0x6
+#define NV50_UNK640000__ESIZE                                   0x1000
+#    define NV50_UNK640000_UNK_000(i)          (0x00640000+(i)*0x1000)
+
+#define NV50_HW_CURSOR                                      0x00647000
+#define NV50_HW_CURSOR__LEN                                        0x2
+#define NV50_HW_CURSOR__ESIZE                                   0x1000
+#    define NV50_HW_CURSOR_POS_CTRL(i)              (0x00647080+(i)*0x1000)
+#    define NV50_HW_CURSOR_POS(i)         (0x00647084+(i)*0x1000)
index d9c6efe..82591c6 100644 (file)
@@ -27,6 +27,7 @@
 #include "drm_sarea.h"
 #include "nouveau_drv.h"
 #include "nouveau_drm.h"
+#include "nv50_kms_wrapper.h"
 
 static int nouveau_init_card_mappings(struct drm_device *dev)
 {
@@ -362,6 +363,13 @@ nouveau_card_init(struct drm_device *dev)
        if (ret) return ret;
 
        dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
+
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               if (dev_priv->card_type >= NV_50) {
+                       nv50_kms_init(dev);
+                       nv50_kms_connector_detect_all(dev);
+               }
+
        return 0;
 }
 
@@ -410,8 +418,7 @@ void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
        nouveau_mem_release(file_priv,dev_priv->pci_heap);
 }
 
-/* first module load, setup the mmio/fb mapping */
-int nouveau_firstopen(struct drm_device *dev)
+int nouveau_setup_mappings(struct drm_device *dev)
 {
 #if defined(__powerpc__)
        struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -457,6 +464,16 @@ int nouveau_firstopen(struct drm_device *dev)
        return 0;
 }
 
+/* first module load, setup the mmio/fb mapping */
+/* KMS: we need mmio at load time, not when the first drm client opens. */
+int nouveau_firstopen(struct drm_device *dev)
+{
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return 0;
+
+       return nouveau_setup_mappings(dev);
+}
+
 #define NV40_CHIPSET_MASK 0x00000baf
 #define NV44_CHIPSET_MASK 0x00005450
 
@@ -549,10 +566,23 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
 
        dev->dev_private = (void *)dev_priv;
 
+       /* init card now, otherwise bad things happen */
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               int rval = 0;
+
+               rval = nouveau_setup_mappings(dev);
+               if (rval != 0)
+                       return rval;
+
+               rval = nouveau_card_init(dev);
+               if (rval != 0)
+                       return rval;
+       }
+
        return 0;
 }
 
-void nouveau_lastclose(struct drm_device *dev)
+void nouveau_close(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
@@ -568,8 +598,22 @@ void nouveau_lastclose(struct drm_device *dev)
        }
 }
 
+/* KMS: we need mmio at load time, not when the first drm client opens. */
+void nouveau_lastclose(struct drm_device *dev)
+{
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return;
+
+       nouveau_close(dev);
+}
+
 int nouveau_unload(struct drm_device *dev)
 {
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               nv50_kms_destroy(dev);
+               nouveau_close(dev);
+       }
+
        drm_free(dev->dev_private, sizeof(*dev->dev_private), DRM_MEM_DRIVER);
        dev->dev_private = NULL;
        return 0;