NV50: Initial import of kernel modesetting.

This commit is contained in:
Maarten Maathuis 2008-06-22 16:29:00 +02:00
parent 7010d50007
commit 473a1997ac
36 changed files with 5503 additions and 19 deletions

View 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)

View 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 \

View 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)
{

View 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);

View 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;
}

View 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);

815
linux-core/nouveau_bios.c Normal file
View file

@ -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;
}

151
linux-core/nouveau_bios.h Normal file
View file

@ -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__ */

View 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);
}

207
linux-core/nv50_connector.c Normal file
View file

@ -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;
}

View file

@ -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__ */

515
linux-core/nv50_crtc.c Normal file
View file

@ -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;
}

67
linux-core/nv50_crtc.h Normal file
View file

@ -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__ */

177
linux-core/nv50_cursor.c Normal file
View file

@ -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;
}

51
linux-core/nv50_cursor.h Normal file
View file

@ -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__ */

177
linux-core/nv50_dac.c Normal file
View file

@ -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;
}

337
linux-core/nv50_display.c Normal file
View file

@ -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;
}

83
linux-core/nv50_display.h Normal file
View file

@ -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__ */

View file

@ -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

136
linux-core/nv50_fb.c Normal file
View file

@ -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;
}

55
linux-core/nv50_fb.h Normal file
View file

@ -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__ */

356
linux-core/nv50_i2c.c Normal file
View file

@ -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);
}

47
linux-core/nv50_i2c.h Normal file
View file

@ -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__ */

View file

@ -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;
}

View file

@ -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__ */

171
linux-core/nv50_lut.c Normal file
View file

@ -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;
}

45
linux-core/nv50_lut.h Normal file
View file

@ -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__ */

33
linux-core/nv50_output.c Normal file
View file

@ -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;
}

60
linux-core/nv50_output.h Normal file
View file

@ -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__ */

204
linux-core/nv50_sor.c Normal file
View file

@ -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;
}

View 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

View 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,

View file

@ -37,6 +37,11 @@
#include "nouveau_reg.h"
#include "nouveau_swmthd.h"
/* needed for interrupt based vpll changes */
#include "nv50_display.h"
#include "nv50_crtc.h"
#include "nv50_output.h"
void
nouveau_irq_preinstall(struct drm_device *dev)
{
@ -503,11 +508,82 @@ static void
nouveau_nv50_display_irq_handler(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t val = NV_READ(NV50_DISPLAY_SUPERVISOR);
uint32_t val = NV_READ(NV50_PDISPLAY_SUPERVISOR);
DRM_INFO("NV50_DISPLAY_INTR - 0x%08X\n", val);
NV_WRITE(NV50_DISPLAY_SUPERVISOR, val);
/* vblank interrupts */
if (val & NV50_PDISPLAY_SUPERVISOR_CRTCn) {
NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CRTCn);
val &= ~NV50_PDISPLAY_SUPERVISOR_CRTCn;
}
/* clock setting amongst other things. */
if (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) {
uint32_t state = (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) >> NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT;
NV50_DEBUG("state %d\n", state);
/* Set pll */
if (state == 2) {
struct nv50_display *display = nv50_get_display(dev);
struct nv50_output *output = NULL;
struct nv50_crtc *crtc = NULL;
int crtc_index;
uint32_t unk30 = NV_READ(NV50_PDISPLAY_UNK30_CTRL);
for (crtc_index = 0; crtc_index < 2; crtc_index++) {
bool clock_change = false;
bool clock_ack = false;
if (crtc_index == 0 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0))
clock_change = true;
if (crtc_index == 1 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1))
clock_change = true;
if (clock_change)
clock_ack = true;
if (display->last_crtc == crtc_index)
clock_ack = true;
list_for_each_entry(crtc, &display->crtcs, head) {
if (crtc->index == crtc_index)
break;
}
if (clock_change)
crtc->set_clock(crtc);
NV50_DEBUG("index %d clock_change %d clock_ack %d\n", crtc_index, clock_change, clock_ack);
if (!clock_ack)
continue;
crtc->set_clock_mode(crtc);
list_for_each_entry(output, &display->outputs, head) {
if (!output->crtc)
continue;
if (output->crtc == crtc)
output->set_clock_mode(output);
}
}
}
NV_WRITE(NV50_PDISPLAY_UNK30_CTRL, NV50_PDISPLAY_UNK30_CTRL_PENDING);
NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK);
val &= ~NV50_PDISPLAY_SUPERVISOR_CLK_MASK;
}
if (val)
DRM_ERROR("unsupported NV50_DISPLAY_INTR - 0x%08X\n", val);
NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val);
}
static void

View 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;

View file

@ -116,6 +116,9 @@
#define NV04_PBUS_PCI_NV_1 0x00001804
#define NV04_PBUS_PCI_NV_19 0x0000184C
#define NV04_PBUS_PCI_NV_20 0x00001850
# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
#define NV04_PTIMER_INTR_0 0x00009100
#define NV04_PTIMER_INTR_EN_0 0x00009140
@ -542,6 +545,8 @@
/* This name is a partial guess. */
#define NV50_DISPLAY_SUPERVISOR 0x00610024
#define NV04_PRAMIN 0x00700000
/* Fifo commands. These are not regs, neither masks */
#define NV03_FIFO_CMD_JUMP 0x20000000
#define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc
@ -591,3 +596,296 @@
#define NV40_RAMFC_UNK_48 0x48
#define NV40_RAMFC_UNK_4C 0x4C
#define NV40_RAMFC_UNK_50 0x50
/* This is a partial import from rules-ng, a few things may be duplicated.
* Eventually we should completely import everything from rules-ng.
* For the moment check rules-ng for docs.
*/
#define NV50_PMC 0x00000000
#define NV50_PMC__LEN 0x1
#define NV50_PMC__ESIZE 0x2000
# define NV50_PMC_BOOT_0 0x00000000
# define NV50_PMC_BOOT_0_REVISION 0x000000ff
# define NV50_PMC_BOOT_0_REVISION__SHIFT 0
# define NV50_PMC_BOOT_0_ARCH 0x0ff00000
# define NV50_PMC_BOOT_0_ARCH__SHIFT 20
# define NV50_PMC_INTR_0 0x00000100
# define NV50_PMC_INTR_0_PFIFO (1<<8)
# define NV50_PMC_INTR_0_PGRAPH (1<<12)
# define NV50_PMC_INTR_0_PTIMER (1<<20)
# define NV50_PMC_INTR_0_HOTPLUG (1<<21)
# define NV50_PMC_INTR_0_DISPLAY (1<<26)
# define NV50_PMC_INTR_EN_0 0x00000140
# define NV50_PMC_INTR_EN_0_MASTER (1<<0)
# define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0)
# define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0)
# define NV50_PMC_ENABLE 0x00000200
# define NV50_PMC_ENABLE_PFIFO (1<<8)
# define NV50_PMC_ENABLE_PGRAPH (1<<12)
#define NV50_PCONNECTOR 0x0000e000
#define NV50_PCONNECTOR__LEN 0x1
#define NV50_PCONNECTOR__ESIZE 0x1000
# define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C4 (1<<4)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C5 (1<<5)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C6 (1<<6)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C7 (1<<7)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C8 (1<<8)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C9 (1<<9)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C10 (1<<10)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C11 (1<<11)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C12 (1<<12)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C13 (1<<13)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C14 (1<<14)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C15 (1<<15)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C4 (1<<20)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C5 (1<<21)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C6 (1<<22)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C7 (1<<23)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C8 (1<<24)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C9 (1<<25)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C10 (1<<26)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C11 (1<<27)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C12 (1<<28)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C13 (1<<29)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C14 (1<<30)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C15 (1<<31)
# define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C4 (1<<4)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C5 (1<<5)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C6 (1<<6)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C7 (1<<7)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C8 (1<<8)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C9 (1<<9)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C10 (1<<10)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C11 (1<<11)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C12 (1<<12)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C13 (1<<13)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C14 (1<<14)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C15 (1<<15)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C4 (1<<20)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C5 (1<<21)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C6 (1<<22)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C7 (1<<23)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C8 (1<<24)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C9 (1<<25)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C10 (1<<26)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C11 (1<<27)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C12 (1<<28)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C13 (1<<29)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C14 (1<<30)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C15 (1<<31)
# define NV50_PCONNECTOR_HOTPLUG_STATE1 0x0000e104
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C0 (1<<2)
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C1 (1<<6)
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C2 (1<<10)
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C3 (1<<14)
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C4 (1<<18)
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C5 (1<<22)
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C6 (1<<26)
# define NV50_PCONNECTOR_HOTPLUG_STATE1_PIN_CONNECTED_I2C7 (1<<30)
# define NV50_PCONNECTOR_HOTPLUG_STATE2 0x0000e108
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C8 (1<<2)
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C9 (1<<6)
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C10 (1<<10)
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C11 (1<<14)
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C12 (1<<18)
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C13 (1<<22)
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C14 (1<<26)
# define NV50_PCONNECTOR_HOTPLUG_STATE2_PIN_CONNECTED_I2C15 (1<<30)
# define NV50_PCONNECTOR_I2C 0x0000e138
# define NV50_PCONNECTOR_I2C__LEN 0x10
# define NV50_PCONNECTOR_I2C__ESIZE 0x18
# define NV50_PCONNECTOR_I2C_PORT(i) (0x0000e138+(i)*0x18)
#define NV50_PBUS 0x00088000
#define NV50_PBUS__LEN 0x1
#define NV50_PBUS__ESIZE 0x1000
# define NV50_PBUS_PCI_ID 0x00088000
# define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff
# define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0
# define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000
# define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16
#define NV50_PFB 0x00100000
#define NV50_PFB__LEN 0x1
#define NV50_PFB__ESIZE 0x1000
#define NV50_PEXTDEV 0x00101000
#define NV50_PEXTDEV__LEN 0x1
#define NV50_PEXTDEV__ESIZE 0x1000
#define NV50_PROM 0x00300000
#define NV50_PROM__LEN 0x1
#define NV50_PROM__ESIZE 0x10000
#define NV50_PGRAPH 0x00400000
#define NV50_PGRAPH__LEN 0x1
#define NV50_PGRAPH__ESIZE 0x10000
#define NV50_PDISPLAY 0x00610000
#define NV50_PDISPLAY__LEN 0x1
#define NV50_PDISPLAY__ESIZE 0x10000
# define NV50_PDISPLAY_SUPERVISOR 0x00610024
# define NV50_PDISPLAY_SUPERVISOR_CRTCn 0x0000000c
# define NV50_PDISPLAY_SUPERVISOR_CRTCn__SHIFT 2
# define NV50_PDISPLAY_SUPERVISOR_CRTC0 (1<<2)
# define NV50_PDISPLAY_SUPERVISOR_CRTC1 (1<<3)
# define NV50_PDISPLAY_SUPERVISOR_CLK_MASK 0x00000070
# define NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT 4
# define NV50_PDISPLAY_SUPERVISOR_CLK_UPDATE (1<<5)
# define NV50_PDISPLAY_SUPERVISOR_INTR 0x0061002c
# define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC0 (1<<2)
# define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC1 (1<<3)
# define NV50_PDISPLAY_SUPERVISOR_INTR_UNK1 (1<<4)
# define NV50_PDISPLAY_SUPERVISOR_INTR_CLK_UPDATE (1<<5)
# define NV50_PDISPLAY_SUPERVISOR_INTR_UNK4 (1<<6)
# define NV50_PDISPLAY_UNK30_CTRL 0x00610030
# define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 (1<<9)
# define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 (1<<10)
# define NV50_PDISPLAY_UNK30_CTRL_PENDING (1<<31)
# define NV50_PDISPLAY_UNK50_CTRL 0x00610050
# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE (1<<1)
# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK 0x00000003
# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK__SHIFT 0
# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE (1<<9)
# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK 0x00000300
# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK__SHIFT 8
# define NV50_PDISPLAY_UNK200_CTRL 0x00610200
# define NV50_PDISPLAY_CURSOR 0x00610270
# define NV50_PDISPLAY_CURSOR__LEN 0x2
# define NV50_PDISPLAY_CURSOR__ESIZE 0x10
# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) (0x00610270+(i)*0x10)
# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON (1<<0)
# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK 0x00030000
# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK__SHIFT 16
# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE (1<<16)
# define NV50_PDISPLAY_CTRL_STATE 0x00610300
# define NV50_PDISPLAY_CTRL_STATE_ENABLE (1<<0)
# define NV50_PDISPLAY_CTRL_STATE_PENDING (1<<31)
# define NV50_PDISPLAY_CTRL_VAL 0x00610304
# define NV50_PDISPLAY_UNK_380 0x00610380
# define NV50_PDISPLAY_RAM_AMOUNT 0x00610384
# define NV50_PDISPLAY_UNK_388 0x00610388
# define NV50_PDISPLAY_UNK_38C 0x0061038c
# define NV50_PDISPLAY_CRTC_VAL 0x00610a00
# define NV50_PDISPLAY_CRTC_VAL__LEN 0x2
# define NV50_PDISPLAY_CRTC_VAL_UNK_900(i,j) (0x00610a18+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_CLUT_MODE(i,j) (0x00610a24+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_INTERLACE(i,j) (0x00610a48+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_SCALE_CTRL(i,j) (0x00610a50+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_CURSOR_CTRL(i,j) (0x00610a58+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_UNK_904(i,j) (0x00610ab8+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_DEPTH(i,j) (0x00610ac8+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_CLOCK(i,j) (0x00610ad0+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_COLOR_CTRL(i,j) (0x00610ae0+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_SYNC_START_TO_BLANK_END(i,j) (0x00610ae8+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_MODE_UNK1(i,j) (0x00610af0+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_DISPLAY_TOTAL(i,j) (0x00610af8+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_SYNC_DURATION(i,j) (0x00610b00+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_MODE_UNK2(i,j) (0x00610b08+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_UNK_828(i,j) (0x00610b10+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_FB_SIZE(i,j) (0x00610b18+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_FB_PITCH(i,j) (0x00610b20+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_FB_PITCH_LINEAR_FB (1<<20)
# define NV50_PDISPLAY_CRTC_VAL_FB_POS(i,j) (0x00610b28+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_SCALE_CENTER_OFFSET(i,j) (0x00610b38+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_REAL_RES(i,j) (0x00610b40+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_SCALE_RES1(i,j) (0x00610b48+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_CRTC_VAL_SCALE_RES2(i,j) (0x00610b50+(i)*0x540+(j)*0x4)
# define NV50_PDISPLAY_DAC_VAL_MODE_CTRL(i,j) (0x00610b58+(i)*0x8+(j)*0x4)
# define NV50_PDISPLAY_SOR_VAL_MODE_CTRL(i,j) (0x00610b70+(i)*0x8+(j)*0x4)
# define NV50_PDISPLAY_DAC_VAL_MODE_CTRL2(i,j) (0x00610bdc+(i)*0x8+(j)*0x4)
# define NV50_PDISPLAY_CRTC_CLK 0x00614000
# define NV50_PDISPLAY_CRTC_CLK__LEN 0x2
# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(i) (0x00614100+(i)*0x800)
# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED 0x00000600
# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED__SHIFT 9
# define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) (0x00614104+(i)*0x800)
# define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) (0x00614108+(i)*0x800)
# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(i) (0x00614200+(i)*0x800)
# define NV50_PDISPLAY_DAC_CLK 0x00614000
# define NV50_PDISPLAY_DAC_CLK__LEN 0x3
# define NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(i) (0x00614280+(i)*0x800)
# define NV50_PDISPLAY_SOR_CLK 0x00614000
# define NV50_PDISPLAY_SOR_CLK__LEN 0x3
# define NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(i) (0x00614300+(i)*0x800)
# define NV50_PDISPLAY_DAC_REGS 0x0061a000
# define NV50_PDISPLAY_DAC_REGS__LEN 0x3
# define NV50_PDISPLAY_DAC_REGS__ESIZE 0x800
# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i) (0x0061a004+(i)*0x800)
# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF (1<<0)
# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF (1<<2)
# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED (1<<4)
# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF (1<<6)
# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING (1<<31)
# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(i) (0x0061a00c+(i)*0x800)
# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE (1<<20)
# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT 0x38000000
# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT__SHIFT 29
# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_DONE (1<<31)
# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i) (0x0061a010+(i)*0x800)
# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED 0x00000600
# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED__SHIFT 9
# define NV50_PDISPLAY_SOR_REGS 0x0061c000
# define NV50_PDISPLAY_SOR_REGS__LEN 0x2
# define NV50_PDISPLAY_SOR_REGS__ESIZE 0x800
# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(i) (0x0061c004+(i)*0x800)
# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON (1<<0)
# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING (1<<31)
# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1(i) (0x0061c008+(i)*0x800)
# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED 0x00000600
# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED__SHIFT 9
# define NV50_PDISPLAY_SOR_REGS_UNK_00C(i) (0x0061c00c+(i)*0x800)
# define NV50_PDISPLAY_SOR_REGS_UNK_010(i) (0x0061c010+(i)*0x800)
# define NV50_PDISPLAY_SOR_REGS_UNK_014(i) (0x0061c014+(i)*0x800)
# define NV50_PDISPLAY_SOR_REGS_UNK_018(i) (0x0061c018+(i)*0x800)
# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i) (0x0061c030+(i)*0x800)
# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE 0x00030000
# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE__SHIFT 16
# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_BLANKED (1<<19)
# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT (1<<28)
#define NV50_UNK640000 0x00640000
#define NV50_UNK640000__LEN 0x6
#define NV50_UNK640000__ESIZE 0x1000
# define NV50_UNK640000_UNK_000(i) (0x00640000+(i)*0x1000)
#define NV50_HW_CURSOR 0x00647000
#define NV50_HW_CURSOR__LEN 0x2
#define NV50_HW_CURSOR__ESIZE 0x1000
# define NV50_HW_CURSOR_POS_CTRL(i) (0x00647080+(i)*0x1000)
# define NV50_HW_CURSOR_POS(i) (0x00647084+(i)*0x1000)

View 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;