diff options
author | Dave Airlie <airlied@redhat.com> | 2011-12-22 11:02:06 +0000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-12-22 11:02:06 +0000 |
commit | 5d56fe5fd794a98c4f446f8665fd06b82e93ff64 (patch) | |
tree | 5d4740d80ecb2e67cf9714d2cc59fbc8405d12ac /drivers/gpu | |
parent | 4cf73129cbe001b41be2f8b56f763fbf3acaa4ce (diff) | |
parent | f7b24c42da1a7bbb98145d27aa716d8af3cae2a6 (diff) | |
download | linux-5d56fe5fd794a98c4f446f8665fd06b82e93ff64.tar.gz linux-5d56fe5fd794a98c4f446f8665fd06b82e93ff64.tar.bz2 linux-5d56fe5fd794a98c4f446f8665fd06b82e93ff64.zip |
Merge branch 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6 into drm-core-next
* 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6: (102 commits)
drm/nouveau/ttm: fix crash as a result of a recent ttm change
drm/nouveau: Fix notifier blocks over the 4GB mark.
drm/nouveau: Fix pushbufs over the 4GB mark.
drm/nvc0/pm: initial engine reclocking
drm/nouveau: move hpd enable/disable to common code
drm/nv40/disp: implement support for hotplug irq
drm/nouveau/gpio: reimplement as nouveau_gpio.c, fixing a number of issues
drm/nouveau: just pass gpio line to pwm_*, not entire gpio struct
drm/nouveau/hwsq: remove some magic, give proper opcode names
drm/nv50/pm: introduce hwsq-based memory reclocking
drm/nv04/disp: handle dual-link spwg panels without needing quirks
drm/nouveau/dp: remove broken display depth function, use the improved one
drm/nouveau/mxm: implement ROM shadow method
drm/nouveau/mxm: implement _DSM shadow method
drm/nouveau/mxm: implement wmi shadow method
drm/nouveau/mxm: initial implementation of dcb sanitisation
drm/nouveau/disp: parse connector info directly in nouveau_connector.c
drm/nouveau/i2c: handle bit-banging ourselves
drm/nouveau/i2c: fix debug message
drm/nouveau/i2c: tidy up bit-bang helpers, also fixing nv50 setsda bug
...
Diffstat (limited to 'drivers/gpu')
71 files changed, 6441 insertions, 2698 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 35ef5b1e3566..9f27e3d9e69a 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -9,9 +9,9 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \ nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ - nouveau_dp.o nouveau_ramht.o \ + nouveau_hdmi.o nouveau_dp.o nouveau_ramht.o \ nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \ - nouveau_mm.o nouveau_vm.o \ + nouveau_mm.o nouveau_vm.o nouveau_mxm.o nouveau_gpio.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ @@ -19,9 +19,12 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ nv40_graph.o nv50_graph.o nvc0_graph.o \ nv40_grctx.o nv50_grctx.o nvc0_grctx.o \ - nv84_crypt.o \ + nv84_crypt.o nv98_crypt.o \ nva3_copy.o nvc0_copy.o \ nv31_mpeg.o nv50_mpeg.o \ + nv84_bsp.o \ + nv84_vp.o \ + nv98_ppp.o \ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ nv04_crtc.o nv04_display.o nv04_cursor.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 5fc201b49d30..e5cbead85e50 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -27,6 +27,7 @@ #include "nouveau_drv.h" #include "nouveau_hw.h" #include "nouveau_encoder.h" +#include "nouveau_gpio.h" #include <linux/io-mapping.h> @@ -34,9 +35,6 @@ #define NV_CIO_CRE_44_HEADA 0x0 #define NV_CIO_CRE_44_HEADB 0x3 #define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */ -#define LEGACY_I2C_CRT 0x80 -#define LEGACY_I2C_PANEL 0x81 -#define LEGACY_I2C_TV 0x82 #define EDID1_LEN 128 @@ -723,115 +721,19 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev) return dcb_entry; } -static int -read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c) -{ - uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4; - int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES; - int recordoffset = 0, rdofs = 1, wrofs = 0; - uint8_t port_type = 0; - - if (!i2ctable) - return -EINVAL; - - if (dcb_version >= 0x30) { - if (i2ctable[0] != dcb_version) /* necessary? */ - NV_WARN(dev, - "DCB I2C table version mismatch (%02X vs %02X)\n", - i2ctable[0], dcb_version); - dcb_i2c_ver = i2ctable[0]; - headerlen = i2ctable[1]; - if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES) - i2c_entries = i2ctable[2]; - else - NV_WARN(dev, - "DCB I2C table has more entries than indexable " - "(%d entries, max %d)\n", i2ctable[2], - DCB_MAX_NUM_I2C_ENTRIES); - entry_len = i2ctable[3]; - /* [4] is i2c_default_indices, read in parse_dcb_table() */ - } - /* - * 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 0; - if (index >= i2c_entries) { - NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n", - index, i2ctable[2]); - return -ENOENT; - } - if (i2ctable[headerlen + entry_len * index + 3] == 0xff) { - NV_ERROR(dev, "DCB I2C entry invalid\n"); - return -EINVAL; - } - - if (dcb_i2c_ver >= 0x30) { - port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index]; - - /* - * Fixup for chips using same address offset for read and - * write. - */ - if (port_type == 4) /* seen on C51 */ - rdofs = wrofs = 1; - if (port_type >= 5) /* G80+ */ - rdofs = wrofs = 0; - } - - if (dcb_i2c_ver >= 0x40) { - if (port_type != 5 && port_type != 6) - NV_WARN(dev, "DCB I2C table has port type %d\n", port_type); - - i2c->entry = ROM32(i2ctable[headerlen + recordoffset + entry_len * index]); - } - - i2c->port_type = port_type; - i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index]; - i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index]; - - return 0; -} - static struct nouveau_i2c_chan * init_i2c_device_find(struct drm_device *dev, int i2c_index) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_table *dcb = &dev_priv->vbios.dcb; - if (i2c_index == 0xff) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct dcb_table *dcb = &dev_priv->vbios.dcb; /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */ - int idx = dcb_entry_idx_from_crtchead(dev), shift = 0; - int default_indices = dcb->i2c_default_indices; + int idx = dcb_entry_idx_from_crtchead(dev); + i2c_index = NV_I2C_DEFAULT(0); if (idx != 0x7f && dcb->entry[idx].i2c_upper_default) - shift = 4; - - i2c_index = (default_indices >> shift) & 0xf; + i2c_index = NV_I2C_DEFAULT(1); } - if (i2c_index == 0x80) /* g80+ */ - i2c_index = dcb->i2c_default_indices & 0xf; - else - if (i2c_index == 0x81) - i2c_index = (dcb->i2c_default_indices & 0xf0) >> 4; - - if (i2c_index >= DCB_MAX_NUM_I2C_ENTRIES) { - NV_ERROR(dev, "invalid i2c_index 0x%x\n", i2c_index); - return NULL; - } - - /* Make sure i2c table entry has been parsed, it may not - * have been if this is a bus not referenced by a DCB encoder - */ - read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, - i2c_index, &dcb->i2c[i2c_index]); return nouveau_i2c_find(dev, i2c_index); } @@ -1199,13 +1101,9 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) switch (cond) { case 0: - { - struct dcb_connector_table_entry *ent = - &bios->dcb.connector.entry[dcb->connector]; - - if (ent->type != DCB_CONNECTOR_eDP) + entry = dcb_conn(dev, dcb->connector); + if (!entry || entry[0] != DCB_CONNECTOR_eDP) iexec->execute = false; - } break; case 1: case 2: @@ -3227,49 +3125,6 @@ init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) return 1; } -static void -init_gpio_unknv50(struct nvbios *bios, struct dcb_gpio_entry *gpio) -{ - const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; - u32 r, s, v; - - /* Not a clue, needs de-magicing */ - r = nv50_gpio_ctl[gpio->line >> 4]; - s = (gpio->line & 0x0f); - v = bios_rd32(bios, r) & ~(0x00010001 << s); - switch ((gpio->entry & 0x06000000) >> 25) { - case 1: - v |= (0x00000001 << s); - break; - case 2: - v |= (0x00010000 << s); - break; - default: - break; - } - - bios_wr32(bios, r, v); -} - -static void -init_gpio_unknvd0(struct nvbios *bios, struct dcb_gpio_entry *gpio) -{ - u32 v, i; - - v = bios_rd32(bios, 0x00d610 + (gpio->line * 4)); - v &= 0xffffff00; - v |= (gpio->entry & 0x00ff0000) >> 16; - bios_wr32(bios, 0x00d610 + (gpio->line * 4), v); - - i = (gpio->entry & 0x1f000000) >> 24; - if (i) { - v = bios_rd32(bios, 0x00d640 + ((i - 1) * 4)); - v &= 0xffffff00; - v |= gpio->line; - bios_wr32(bios, 0x00d640 + ((i - 1) * 4), v); - } -} - static int init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) { @@ -3282,35 +3137,8 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) * each GPIO according to various values listed in each entry */ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - int i; - - if (dev_priv->card_type < NV_50) { - NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n"); - return 1; - } - - if (!iexec->execute) - return 1; - - for (i = 0; i < bios->dcb.gpio.entries; i++) { - struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i]; - - BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry); - - BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n", - offset, gpio->tag, gpio->state_default); - - if (!bios->execute) - continue; - - pgpio->set(bios->dev, gpio->tag, gpio->state_default); - if (dev_priv->card_type < NV_D0) - init_gpio_unknv50(bios, gpio); - else - init_gpio_unknvd0(bios, gpio); - } + if (iexec->execute && bios->execute) + nouveau_gpio_reset(bios->dev); return 1; } @@ -4407,18 +4235,6 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b break; } - /* Dell Latitude D620 reports a too-high value for the dual-link - * transition freq, causing us to program the panel incorrectly. - * - * It doesn't appear the VBIOS actually uses its transition freq - * (90000kHz), instead it uses the "Number of LVDS channels" field - * out of the panel ID structure (http://www.spwg.org/). - * - * For the moment, a quirk will do :) - */ - if (nv_match_device(dev, 0x01d7, 0x1028, 0x01c2)) - bios->fp.duallink_transition_clk = 80000; - /* set dual_link flag for EDID case */ if (pxclk && (chip_version < 0x25 || chip_version > 0x28)) bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk); @@ -4541,7 +4357,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk, NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n", dcbent->type, dcbent->location, dcbent->or); for (i = 0; i < table[3]; i++) { - otable = ROMPTR(bios, table[table[1] + (i * table[2])]); + otable = ROMPTR(dev, table[table[1] + (i * table[2])]); if (otable && bios_encoder_match(dcbent, ROM32(otable[0]))) break; } @@ -4719,7 +4535,7 @@ static struct pll_mapping nv84_pll_mapping[] = { { PLL_CORE , 0x004028 }, { PLL_SHADER, 0x004020 }, { PLL_MEMORY, 0x004008 }, - { PLL_UNK05 , 0x004030 }, + { PLL_VDEC , 0x004030 }, { PLL_UNK41 , 0x00e818 }, { PLL_VPLL0 , 0x614100 }, { PLL_VPLL1 , 0x614900 }, @@ -5485,6 +5301,9 @@ bit_table(struct drm_device *dev, u8 id, struct bit_entry *bit) struct nvbios *bios = &dev_priv->vbios; u8 entries, *entry; + if (bios->type != NVBIOS_BIT) + return -ENODEV; + entries = bios->data[bios->offset + 10]; entry = &bios->data[bios->offset + 12]; while (entries--) { @@ -5493,7 +5312,7 @@ bit_table(struct drm_device *dev, u8 id, struct bit_entry *bit) bit->version = entry[1]; bit->length = ROM16(entry[2]); bit->offset = ROM16(entry[4]); - bit->data = ROMPTR(bios, entry[4]); + bit->data = ROMPTR(dev, entry[4]); return 0; } @@ -5598,10 +5417,6 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi uint16_t legacy_scripts_offset, legacy_i2c_offset; /* load needed defaults in case we can't parse this info */ - bios->dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX; - bios->dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX; - bios->dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX; - bios->dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX; bios->digital_min_front_porch = 0x4b; bios->fmaxvco = 256000; bios->fminvco = 128000; @@ -5709,14 +5524,6 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset]; bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1]; bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2]; - if (bios->data[legacy_i2c_offset + 4]) - bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4]; - if (bios->data[legacy_i2c_offset + 5]) - bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5]; - if (bios->data[legacy_i2c_offset + 6]) - bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6]; - if (bios->data[legacy_i2c_offset + 7]) - bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7]; if (bmplength > 74) { bios->fmaxvco = ROM32(bmp[67]); @@ -5767,286 +5574,128 @@ static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) return 0; } -static struct dcb_gpio_entry * -new_gpio_entry(struct nvbios *bios) -{ - struct drm_device *dev = bios->dev; - struct dcb_gpio_table *gpio = &bios->dcb.gpio; - - if (gpio->entries >= DCB_MAX_NUM_GPIO_ENTRIES) { - NV_ERROR(dev, "exceeded maximum number of gpio entries!!\n"); - return NULL; - } - - return &gpio->entry[gpio->entries++]; -} - -struct dcb_gpio_entry * -nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag) +void * +dcb_table(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - int i; - - for (i = 0; i < bios->dcb.gpio.entries; i++) { - if (bios->dcb.gpio.entry[i].tag != tag) - continue; + u8 *dcb = NULL; - return &bios->dcb.gpio.entry[i]; + if (dev_priv->card_type > NV_04) + dcb = ROMPTR(dev, dev_priv->vbios.data[0x36]); + if (!dcb) { + NV_WARNONCE(dev, "No DCB data found in VBIOS\n"); + return NULL; } - return NULL; -} - -static void -parse_dcb_gpio_table(struct nvbios *bios) -{ - struct drm_device *dev = bios->dev; - struct dcb_gpio_entry *e; - u8 headerlen, entries, recordlen; - u8 *dcb, *gpio = NULL, *entry; - int i; - - dcb = ROMPTR(bios, bios->data[0x36]); + if (dcb[0] >= 0x41) { + NV_WARNONCE(dev, "DCB version 0x%02x unknown\n", dcb[0]); + return NULL; + } else if (dcb[0] >= 0x30) { - gpio = ROMPTR(bios, dcb[10]); - if (!gpio) - goto no_table; - - headerlen = gpio[1]; - entries = gpio[2]; - recordlen = gpio[3]; + if (ROM32(dcb[6]) == 0x4edcbdcb) + return dcb; } else - if (dcb[0] >= 0x22 && dcb[-1] >= 0x13) { - gpio = ROMPTR(bios, dcb[-15]); - if (!gpio) - goto no_table; - - headerlen = 3; - entries = gpio[2]; - recordlen = gpio[1]; + if (dcb[0] >= 0x20) { + if (ROM32(dcb[4]) == 0x4edcbdcb) + return dcb; } else - if (dcb[0] >= 0x22) { - /* No GPIO table present, parse the TVDAC GPIO data. */ - uint8_t *tvdac_gpio = &dcb[-5]; - - if (tvdac_gpio[0] & 1) { - e = new_gpio_entry(bios); - e->tag = DCB_GPIO_TVDAC0; - e->line = tvdac_gpio[1] >> 4; - e->invert = tvdac_gpio[0] & 2; - } - - goto no_table; + if (dcb[0] >= 0x15) { + if (!memcmp(&dcb[-7], "DEV_REC", 7)) + return dcb; } else { - NV_DEBUG(dev, "no/unknown gpio table on DCB 0x%02x\n", dcb[0]); - goto no_table; - } - - entry = gpio + headerlen; - for (i = 0; i < entries; i++, entry += recordlen) { - e = new_gpio_entry(bios); - if (!e) - break; - - if (gpio[0] < 0x40) { - e->entry = ROM16(entry[0]); - e->tag = (e->entry & 0x07e0) >> 5; - if (e->tag == 0x3f) { - bios->dcb.gpio.entries--; - continue; - } - - e->line = (e->entry & 0x001f); - e->invert = ((e->entry & 0xf800) >> 11) != 4; - } else { - e->entry = ROM32(entry[0]); - e->tag = (e->entry & 0x0000ff00) >> 8; - if (e->tag == 0xff) { - bios->dcb.gpio.entries--; - continue; - } - - e->line = (e->entry & 0x0000001f) >> 0; - if (gpio[0] == 0x40) { - e->state_default = (e->entry & 0x01000000) >> 24; - e->state[0] = (e->entry & 0x18000000) >> 27; - e->state[1] = (e->entry & 0x60000000) >> 29; - } else { - e->state_default = (e->entry & 0x00000080) >> 7; - e->state[0] = (entry[4] >> 4) & 3; - e->state[1] = (entry[4] >> 6) & 3; - } - } - } - -no_table: - /* Apple iMac G4 NV18 */ - if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) { - e = new_gpio_entry(bios); - if (e) { - e->tag = DCB_GPIO_TVDAC0; - e->line = 4; - } - } -} - -struct dcb_connector_table_entry * -nouveau_bios_connector_entry(struct drm_device *dev, int index) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - struct dcb_connector_table_entry *cte; - - if (index >= bios->dcb.connector.entries) - return NULL; - - cte = &bios->dcb.connector.entry[index]; - if (cte->type == 0xff) + /* + * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but + * always has the same single (crt) entry, even when tv-out + * present, so the conclusion is this version cannot really + * be used. + * + * v1.2 tables (some NV6/10, and NV15+) normally have the + * same 5 entries, which are not specific to the card and so + * no use. + * + * v1.2 does have an I2C table that read_dcb_i2c_table can + * handle, but cards exist (nv11 in #14821) with a bad i2c + * table pointer, so use the indices parsed in + * parse_bmp_structure. + * + * v1.1 (NV5+, maybe some NV4) is entirely unhelpful + */ + NV_WARNONCE(dev, "No useful DCB data in VBIOS\n"); return NULL; + } - return cte; + NV_WARNONCE(dev, "DCB header validation failed\n"); + return NULL; } -static enum dcb_connector_type -divine_connector_type(struct nvbios *bios, int index) +void * +dcb_outp(struct drm_device *dev, u8 idx) { - struct dcb_table *dcb = &bios->dcb; - unsigned encoders = 0, type = DCB_CONNECTOR_NONE; - int i; - - for (i = 0; i < dcb->entries; i++) { - if (dcb->entry[i].connector == index) - encoders |= (1 << dcb->entry[i].type); - } - - if (encoders & (1 << OUTPUT_DP)) { - if (encoders & (1 << OUTPUT_TMDS)) - type = DCB_CONNECTOR_DP; - else - type = DCB_CONNECTOR_eDP; - } else - if (encoders & (1 << OUTPUT_TMDS)) { - if (encoders & (1 << OUTPUT_ANALOG)) - type = DCB_CONNECTOR_DVI_I; - else - type = DCB_CONNECTOR_DVI_D; - } else - if (encoders & (1 << OUTPUT_ANALOG)) { - type = DCB_CONNECTOR_VGA; + u8 *dcb = dcb_table(dev); + if (dcb && dcb[0] >= 0x30) { + if (idx < dcb[2]) + return dcb + dcb[1] + (idx * dcb[3]); } else - if (encoders & (1 << OUTPUT_LVDS)) { - type = DCB_CONNECTOR_LVDS; + if (dcb && dcb[0] >= 0x20) { + u8 *i2c = ROMPTR(dev, dcb[2]); + u8 *ent = dcb + 8 + (idx * 8); + if (i2c && ent < i2c) + return ent; } else - if (encoders & (1 << OUTPUT_TV)) { - type = DCB_CONNECTOR_TV_0; + if (dcb && dcb[0] >= 0x15) { + u8 *i2c = ROMPTR(dev, dcb[2]); + u8 *ent = dcb + 4 + (idx * 10); + if (i2c && ent < i2c) + return ent; } - return type; + return NULL; } -static void -apply_dcb_connector_quirks(struct nvbios *bios, int idx) -{ - struct dcb_connector_table_entry *cte = &bios->dcb.connector.entry[idx]; - struct drm_device *dev = bios->dev; +int +dcb_outp_foreach(struct drm_device *dev, void *data, + int (*exec)(struct drm_device *, void *, int idx, u8 *outp)) +{ + int ret, idx = -1; + u8 *outp = NULL; + while ((outp = dcb_outp(dev, ++idx))) { + if (ROM32(outp[0]) == 0x00000000) + break; /* seen on an NV11 with DCB v1.5 */ + if (ROM32(outp[0]) == 0xffffffff) + break; /* seen on an NV17 with DCB v2.0 */ + + if ((outp[0] & 0x0f) == OUTPUT_UNUSED) + continue; + if ((outp[0] & 0x0f) == OUTPUT_EOL) + break; - /* Gigabyte NX85T */ - if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { - if (cte->type == DCB_CONNECTOR_HDMI_1) - cte->type = DCB_CONNECTOR_DVI_I; + ret = exec(dev, data, idx, outp); + if (ret) + return ret; } - /* Gigabyte GV-NX86T512H */ - if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { - if (cte->type == DCB_CONNECTOR_HDMI_1) - cte->type = DCB_CONNECTOR_DVI_I; - } + return 0; } -static const u8 hpd_gpio[16] = { - 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60, -}; - -static void -parse_dcb_connector_table(struct nvbios *bios) +u8 * +dcb_conntab(struct drm_device *dev) { - struct drm_device *dev = bios->dev; - struct dcb_connector_table *ct = &bios->dcb.connector; - struct dcb_connector_table_entry *cte; - uint8_t *conntab = &bios->data[bios->dcb.connector_table_ptr]; - uint8_t *entry; - int i; - - if (!bios->dcb.connector_table_ptr) { - NV_DEBUG_KMS(dev, "No DCB connector table present\n"); - return; - } - - NV_INFO(dev, "DCB connector table: VHER 0x%02x %d %d %d\n", - conntab[0], conntab[1], conntab[2], conntab[3]); - if ((conntab[0] != 0x30 && conntab[0] != 0x40) || - (conntab[3] != 2 && conntab[3] != 4)) { - NV_ERROR(dev, " Unknown! Please report.\n"); - return; + u8 *dcb = dcb_table(dev); + if (dcb && dcb[0] >= 0x30 && dcb[1] >= 0x16) { + u8 *conntab = ROMPTR(dev, dcb[0x14]); + if (conntab && conntab[0] >= 0x30 && conntab[0] <= 0x40) + return conntab; } + return NULL; +} - ct->entries = conntab[2]; - - entry = conntab + conntab[1]; - cte = &ct->entry[0]; - for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) { - cte->index = i; - if (conntab[3] == 2) - cte->entry = ROM16(entry[0]); - else - cte->entry = ROM32(entry[0]); - - cte->type = (cte->entry & 0x000000ff) >> 0; - cte->index2 = (cte->entry & 0x00000f00) >> 8; - - cte->gpio_tag = ffs((cte->entry & 0x07033000) >> 12); - cte->gpio_tag = hpd_gpio[cte->gpio_tag]; - - if (cte->type == 0xff) - continue; - - apply_dcb_connector_quirks(bios, i); - - NV_INFO(dev, " %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n", - i, cte->entry, cte->type, cte->index, cte->gpio_tag); - - /* check for known types, fallback to guessing the type - * from attached encoders if we hit an unknown. - */ - switch (cte->type) { - case DCB_CONNECTOR_VGA: - case DCB_CONNECTOR_TV_0: - case DCB_CONNECTOR_TV_1: - case DCB_CONNECTOR_TV_3: - case DCB_CONNECTOR_DVI_I: - case DCB_CONNECTOR_DVI_D: - case DCB_CONNECTOR_LVDS: - case DCB_CONNECTOR_LVDS_SPWG: - case DCB_CONNECTOR_DP: - case DCB_CONNECTOR_eDP: - case DCB_CONNECTOR_HDMI_0: - case DCB_CONNECTOR_HDMI_1: - break; - default: - cte->type = divine_connector_type(bios, cte->index); - NV_WARN(dev, "unknown type, using 0x%02x\n", cte->type); - break; - } - - if (nouveau_override_conntype) { - int type = divine_connector_type(bios, cte->index); - if (type != cte->type) - NV_WARN(dev, " -> type 0x%02x\n", cte->type); - } - - } +u8 * +dcb_conn(struct drm_device *dev, u8 idx) +{ + u8 *conntab = dcb_conntab(dev); + if (conntab && idx < conntab[2]) + return conntab + conntab[1] + (idx * conntab[3]); + return NULL; } static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb) @@ -6079,8 +5728,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, entry->type = conn & 0xf; entry->i2c_index = (conn >> 4) & 0xf; entry->heads = (conn >> 8) & 0xf; - if (dcb->version >= 0x40) - entry->connector = (conn >> 12) & 0xf; + entry->connector = (conn >> 12) & 0xf; entry->bus = (conn >> 16) & 0xf; entry->location = (conn >> 20) & 0x3; entry->or = (conn >> 24) & 0xf; @@ -6252,25 +5900,6 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb, return true; } -static bool parse_dcb_entry(struct drm_device *dev, struct dcb_table *dcb, - uint32_t conn, uint32_t conf) -{ - struct dcb_entry *entry = new_dcb_entry(dcb); - bool ret; - - if (dcb->version >= 0x20) - ret = parse_dcb20_entry(dev, dcb, conn, conf, entry); - else - ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); - if (!ret) - return ret; - - read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, - entry->i2c_index, &dcb->i2c[entry->i2c_index]); - - return true; -} - static void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb) { @@ -6431,154 +6060,118 @@ fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios) #endif /* Make up some sane defaults */ - fabricate_dcb_output(dcb, OUTPUT_ANALOG, LEGACY_I2C_CRT, 1, 1); + fabricate_dcb_output(dcb, OUTPUT_ANALOG, + bios->legacy.i2c_indices.crt, 1, 1); if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0) - fabricate_dcb_output(dcb, OUTPUT_TV, LEGACY_I2C_TV, + fabricate_dcb_output(dcb, OUTPUT_TV, + bios->legacy.i2c_indices.tv, all_heads, 0); else if (bios->tmds.output0_script_ptr || bios->tmds.output1_script_ptr) - fabricate_dcb_output(dcb, OUTPUT_TMDS, LEGACY_I2C_PANEL, + fabricate_dcb_output(dcb, OUTPUT_TMDS, + bios->legacy.i2c_indices.panel, all_heads, 1); } static int -parse_dcb_table(struct drm_device *dev, struct nvbios *bios) +parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_table *dcb = &bios->dcb; - uint16_t dcbptr = 0, i2ctabptr = 0; - uint8_t *dcbtable; - uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES; - bool configblock = true; - int recordlength = 8, confofs = 4; - int i; - - /* get the offset from 0x36 */ - if (dev_priv->card_type > NV_04) { - dcbptr = ROM16(bios->data[0x36]); - if (dcbptr == 0x0000) - NV_WARN(dev, "No output data (DCB) found in BIOS\n"); - } - - /* this situation likely means a really old card, pre DCB */ - if (dcbptr == 0x0) { - fabricate_dcb_encoder_table(dev, bios); - return 0; - } - - dcbtable = &bios->data[dcbptr]; - - /* get DCB version */ - dcb->version = dcbtable[0]; - NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n", - dcb->version >> 4, dcb->version & 0xf); - - if (dcb->version >= 0x20) { /* NV17+ */ - uint32_t sig; + struct dcb_table *dcb = &dev_priv->vbios.dcb; + u32 conf = (dcb->version >= 0x20) ? ROM32(outp[4]) : ROM32(outp[6]); + u32 conn = ROM32(outp[0]); + bool ret; - if (dcb->version >= 0x30) { /* NV40+ */ - headerlen = dcbtable[1]; - entries = dcbtable[2]; - recordlength = dcbtable[3]; - i2ctabptr = ROM16(dcbtable[4]); - sig = ROM32(dcbtable[6]); - dcb->gpio_table_ptr = ROM16(dcbtable[10]); - dcb->connector_table_ptr = ROM16(dcbtable[20]); - } else { - i2ctabptr = ROM16(dcbtable[2]); - sig = ROM32(dcbtable[4]); - headerlen = 8; - } + if (apply_dcb_encoder_quirks(dev, idx, &conn, &conf)) { + struct dcb_entry *entry = new_dcb_entry(dcb); - if (sig != 0x4edcbdcb) { - NV_ERROR(dev, "Bad Display Configuration Block " - "signature (%08X)\n", sig); - return -EINVAL; - } - } else if (dcb->version >= 0x15) { /* some NV11 and NV20 */ - char sig[8] = { 0 }; + NV_TRACEWARN(dev, "DCB outp %02d: %08x %08x\n", idx, conn, conf); - strncpy(sig, (char *)&dcbtable[-7], 7); - i2ctabptr = ROM16(dcbtable[2]); - recordlength = 10; - confofs = 6; + if (dcb->version >= 0x20) + ret = parse_dcb20_entry(dev, dcb, conn, conf, entry); + else + ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); + if (!ret) + return 1; /* stop parsing */ - if (strcmp(sig, "DEV_REC")) { - NV_ERROR(dev, "Bad Display Configuration Block " - "signature (%s)\n", sig); - return -EINVAL; - } - } else { - /* - * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always - * has the same single (crt) entry, even when tv-out present, so - * the conclusion is this version cannot really be used. - * v1.2 tables (some NV6/10, and NV15+) normally have the same - * 5 entries, which are not specific to the card and so no use. - * v1.2 does have an I2C table that read_dcb_i2c_table can - * handle, but cards exist (nv11 in #14821) with a bad i2c table - * pointer, so use the indices parsed in parse_bmp_structure. - * v1.1 (NV5+, maybe some NV4) is entirely unhelpful + /* Ignore the I2C index for on-chip TV-out, as there + * are cards with bogus values (nv31m in bug 23212), + * and it's otherwise useless. */ - NV_TRACEWARN(dev, "No useful information in BIOS output table; " - "adding all possible outputs\n"); - fabricate_dcb_encoder_table(dev, bios); - return 0; + if (entry->type == OUTPUT_TV && + entry->location == DCB_LOC_ON_CHIP) + entry->i2c_index = 0x0f; } - if (!i2ctabptr) - NV_WARN(dev, "No pointer to DCB I2C port table\n"); - else { - dcb->i2c_table = &bios->data[i2ctabptr]; - if (dcb->version >= 0x30) - dcb->i2c_default_indices = dcb->i2c_table[4]; + return 0; +} - /* - * Parse the "management" I2C bus, used for hardware - * monitoring and some external TMDS transmitters. - */ - if (dcb->version >= 0x22) { - int idx = (dcb->version >= 0x40 ? - dcb->i2c_default_indices & 0xf : - 2); +static void +dcb_fake_connectors(struct nvbios *bios) +{ + struct dcb_table *dcbt = &bios->dcb; + u8 map[16] = { }; + int i, idx = 0; - read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, - idx, &dcb->i2c[idx]); - } + /* heuristic: if we ever get a non-zero connector field, assume + * that all the indices are valid and we don't need fake them. + */ + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector) + return; } - if (entries > DCB_MAX_NUM_ENTRIES) - entries = DCB_MAX_NUM_ENTRIES; - - for (i = 0; i < entries; i++) { - uint32_t connection, config = 0; - - connection = ROM32(dcbtable[headerlen + recordlength * i]); - if (configblock) - config = ROM32(dcbtable[headerlen + confofs + recordlength * i]); - - /* seen on an NV11 with DCB v1.5 */ - if (connection == 0x00000000) - break; + /* no useful connector info available, we need to make it up + * ourselves. the rule here is: anything on the same i2c bus + * is considered to be on the same connector. any output + * without an associated i2c bus is assigned its own unique + * connector index. + */ + for (i = 0; i < dcbt->entries; i++) { + u8 i2c = dcbt->entry[i].i2c_index; + if (i2c == 0x0f) { + dcbt->entry[i].connector = idx++; + } else { + if (!map[i2c]) + map[i2c] = ++idx; + dcbt->entry[i].connector = map[i2c] - 1; + } + } - /* seen on an NV17 with DCB v2.0 */ - if (connection == 0xffffffff) - break; + /* if we created more than one connector, destroy the connector + * table - just in case it has random, rather than stub, entries. + */ + if (i > 1) { + u8 *conntab = dcb_conntab(bios->dev); + if (conntab) + conntab[0] = 0x00; + } +} - if ((connection & 0x0000000f) == 0x0000000f) - continue; +static int +parse_dcb_table(struct drm_device *dev, struct nvbios *bios) +{ + struct dcb_table *dcb = &bios->dcb; + u8 *dcbt, *conn; + int idx; + + dcbt = dcb_table(dev); + if (!dcbt) { + /* handle pre-DCB boards */ + if (bios->type == NVBIOS_BMP) { + fabricate_dcb_encoder_table(dev, bios); + return 0; + } - if (!apply_dcb_encoder_quirks(dev, i, &connection, &config)) - continue; + return -EINVAL; + } - NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n", - dcb->entries, connection, config); + NV_TRACE(dev, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf); - if (!parse_dcb_entry(dev, dcb, connection, config)) - break; - } + dcb->version = dcbt[0]; + dcb_outp_foreach(dev, NULL, parse_dcb_entry); /* * apart for v2.1+ not being known for requiring merging, this @@ -6590,77 +6183,19 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) if (!dcb->entries) return -ENXIO; - parse_dcb_gpio_table(bios); - parse_dcb_connector_table(bios); - return 0; -} - -static void -fixup_legacy_connector(struct nvbios *bios) -{ - struct dcb_table *dcb = &bios->dcb; - int i, i2c, i2c_conn[DCB_MAX_NUM_I2C_ENTRIES] = { }; - - /* - * DCB 3.0 also has the table in most cases, but there are some cards - * where the table is filled with stub entries, and the DCB entriy - * indices are all 0. We don't need the connector indices on pre-G80 - * chips (yet?) so limit the use to DCB 4.0 and above. - */ - if (dcb->version >= 0x40) - return; - - dcb->connector.entries = 0; - - /* - * No known connector info before v3.0, so make it up. the rule here - * is: anything on the same i2c bus is considered to be on the same - * connector. any output without an associated i2c bus is assigned - * its own unique connector index. - */ - for (i = 0; i < dcb->entries; i++) { - /* - * Ignore the I2C index for on-chip TV-out, as there - * are cards with bogus values (nv31m in bug 23212), - * and it's otherwise useless. - */ - if (dcb->entry[i].type == OUTPUT_TV && - dcb->entry[i].location == DCB_LOC_ON_CHIP) - dcb->entry[i].i2c_index = 0xf; - i2c = dcb->entry[i].i2c_index; - - if (i2c_conn[i2c]) { - dcb->entry[i].connector = i2c_conn[i2c] - 1; - continue; + /* dump connector table entries to log, if any exist */ + idx = -1; + while ((conn = dcb_conn(dev, ++idx))) { + if (conn[0] != 0xff) { + NV_TRACE(dev, "DCB conn %02d: ", idx); + if (dcb_conntab(dev)[3] < 4) + printk("%04x\n", ROM16(conn[0])); + else + printk("%08x\n", ROM32(conn[0])); } - - dcb->entry[i].connector = dcb->connector.entries++; - if (i2c != 0xf) - i2c_conn[i2c] = dcb->connector.entries; - } - - /* Fake the connector table as well as just connector indices */ - for (i = 0; i < dcb->connector.entries; i++) { - dcb->connector.entry[i].index = i; - dcb->connector.entry[i].type = divine_connector_type(bios, i); - dcb->connector.entry[i].gpio_tag = 0xff; - } -} - -static void -fixup_legacy_i2c(struct nvbios *bios) -{ - struct dcb_table *dcb = &bios->dcb; - int i; - - for (i = 0; i < dcb->entries; i++) { - if (dcb->entry[i].i2c_index == LEGACY_I2C_CRT) - dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt; - if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL) - dcb->entry[i].i2c_index = bios->legacy.i2c_indices.panel; - if (dcb->entry[i].i2c_index == LEGACY_I2C_TV) - dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv; } + dcb_fake_connectors(bios); + return 0; } static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry) @@ -6879,19 +6414,6 @@ nouveau_run_vbios_init(struct drm_device *dev) return ret; } -static void -nouveau_bios_i2c_devices_takedown(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - struct dcb_i2c_entry *entry; - int i; - - entry = &bios->dcb.i2c[0]; - for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++) - nouveau_i2c_fini(dev, entry); -} - static bool nouveau_bios_posted(struct drm_device *dev) { @@ -6928,12 +6450,17 @@ nouveau_bios_init(struct drm_device *dev) if (ret) return ret; - ret = parse_dcb_table(dev, bios); + ret = nouveau_i2c_init(dev); if (ret) return ret; - fixup_legacy_i2c(bios); - fixup_legacy_connector(bios); + ret = nouveau_mxm_init(dev); + if (ret) + return ret; + + ret = parse_dcb_table(dev, bios); + if (ret) + return ret; if (!bios->major_version) /* we don't run version 0 bios */ return 0; @@ -6971,5 +6498,6 @@ nouveau_bios_init(struct drm_device *dev) void nouveau_bios_takedown(struct drm_device *dev) { - nouveau_bios_i2c_devices_takedown(dev); + nouveau_mxm_fini(dev); + nouveau_i2c_fini(dev); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 8adb69e4a6b1..1e382ad5a2b8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -34,9 +34,14 @@ #define DCB_LOC_ON_CHIP 0 -#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x)) -#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x)) -#define ROMPTR(bios, x) (ROM16(x) ? &(bios)->data[ROM16(x)] : NULL) +#define ROM16(x) le16_to_cpu(*(u16 *)&(x)) +#define ROM32(x) le32_to_cpu(*(u32 *)&(x)) +#define ROM48(x) ({ u8 *p = &(x); (u64)ROM16(p[4]) << 32 | ROM32(p[0]); }) +#define ROM64(x) le64_to_cpu(*(u64 *)&(x)) +#define ROMPTR(d,x) ({ \ + struct drm_nouveau_private *dev_priv = (d)->dev_private; \ + ROM16(x) ? &dev_priv->vbios.data[ROM16(x)] : NULL; \ +}) struct bit_entry { uint8_t id; @@ -48,30 +53,12 @@ struct bit_entry { int bit_table(struct drm_device *, u8 id, struct bit_entry *); -struct dcb_i2c_entry { - uint32_t entry; - uint8_t port_type; - uint8_t read, write; - struct nouveau_i2c_chan *chan; -}; - enum dcb_gpio_tag { DCB_GPIO_TVDAC0 = 0xc, DCB_GPIO_TVDAC1 = 0x2d, -}; - -struct dcb_gpio_entry { - enum dcb_gpio_tag tag; - int line; - bool invert; - uint32_t entry; - uint8_t state_default; - uint8_t state[2]; -}; - -struct dcb_gpio_table { - int entries; - struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES]; + DCB_GPIO_PWM_FAN = 0x9, + DCB_GPIO_FAN_SENSE = 0x3d, + DCB_GPIO_UNUSED = 0xff }; enum dcb_connector_type { @@ -90,20 +77,6 @@ enum dcb_connector_type { DCB_CONNECTOR_NONE = 0xff }; -struct dcb_connector_table_entry { - uint8_t index; - uint32_t entry; - enum dcb_connector_type type; - uint8_t index2; - uint8_t gpio_tag; - void *drm; -}; - -struct dcb_connector_table { - int entries; - struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES]; -}; - enum dcb_type { OUTPUT_ANALOG = 0, OUTPUT_TV = 1, @@ -111,6 +84,7 @@ enum dcb_type { OUTPUT_LVDS = 3, OUTPUT_DP = 6, OUTPUT_EOL = 14, /* DCB 4.0+, appears to be end-of-list */ + OUTPUT_UNUSED = 15, OUTPUT_ANY = -1 }; @@ -155,18 +129,8 @@ struct dcb_entry { struct dcb_table { uint8_t version; - int entries; struct dcb_entry entry[DCB_MAX_NUM_ENTRIES]; - - uint8_t *i2c_table; - uint8_t i2c_default_indices; - struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES]; - - uint16_t gpio_table_ptr; - struct dcb_gpio_table gpio; - uint16_t connector_table_ptr; - struct dcb_connector_table connector; }; enum nouveau_or { @@ -195,7 +159,7 @@ enum pll_types { PLL_SHADER = 0x02, PLL_UNK03 = 0x03, PLL_MEMORY = 0x04, - PLL_UNK05 = 0x05, + PLL_VDEC = 0x05, PLL_UNK40 = 0x40, PLL_UNK41 = 0x41, PLL_UNK42 = 0x42, @@ -333,4 +297,11 @@ struct nvbios { } legacy; }; +void *dcb_table(struct drm_device *); +void *dcb_outp(struct drm_device *, u8 idx); +int dcb_outp_foreach(struct drm_device *, void *data, + int (*)(struct drm_device *, void *, int idx, u8 *outp)); +u8 *dcb_conntab(struct drm_device *); +u8 *dcb_conn(struct drm_device *, u8 idx); + #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index f12dd0f39211..8cf4a48f872e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -682,8 +682,7 @@ nouveau_vma_getmap(struct nouveau_channel *chan, struct nouveau_bo *nvbo, if (mem->mem_type == TTM_PL_VRAM) nouveau_vm_map(vma, node); else - nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT, - node, node->pages); + nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT, node); return 0; } @@ -810,7 +809,6 @@ out: static void nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) { - struct nouveau_mem *node = new_mem->mm_node; struct nouveau_bo *nvbo = nouveau_bo(bo); struct nouveau_vma *vma; @@ -822,7 +820,7 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) nvbo->page_shift == vma->vm->spg_shift) { nouveau_vm_map_sg(vma, 0, new_mem-> num_pages << PAGE_SHIFT, - node, node->pages); + new_mem->mm_node); } else { nouveau_vm_unmap(vma); } @@ -1173,7 +1171,7 @@ nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm, nouveau_vm_map(vma, nvbo->bo.mem.mm_node); else if (nvbo->bo.mem.mem_type == TTM_PL_TT) - nouveau_vm_map_sg(vma, 0, size, node, node->pages); + nouveau_vm_map_sg(vma, 0, size, node); list_add_tail(&vma->head, &nvbo->vma_list); vma->refcount = 1; diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index bb6ec9ef8676..a018defb7621 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -187,6 +187,8 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, nouveau_dma_pre_init(chan); chan->user_put = 0x40; chan->user_get = 0x44; + if (dev_priv->card_type >= NV_50) + chan->user_get_hi = 0x60; /* disable the fifo caches */ pfifo->reassign(dev, false); diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index cea6696b1906..f3ce34be082a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -35,6 +35,7 @@ #include "nouveau_encoder.h" #include "nouveau_crtc.h" #include "nouveau_connector.h" +#include "nouveau_gpio.h" #include "nouveau_hw.h" static void nouveau_connector_hotplug(void *, int); @@ -78,29 +79,11 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder) return NULL; } -/*TODO: This could use improvement, and learn to handle the fixed - * BIOS tables etc. It's fine currently, for its only user. - */ -int -nouveau_connector_bpp(struct drm_connector *connector) -{ - struct nouveau_connector *nv_connector = nouveau_connector(connector); - - if (nv_connector->edid && nv_connector->edid->revision >= 4) { - u8 bpc = ((nv_connector->edid->input & 0x70) >> 3) + 4; - if (bpc > 4) - return bpc; - } - - return 18; -} - static void nouveau_connector_destroy(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); struct drm_nouveau_private *dev_priv; - struct nouveau_gpio_engine *pgpio; struct drm_device *dev; if (!nv_connector) @@ -110,10 +93,9 @@ nouveau_connector_destroy(struct drm_connector *connector) dev_priv = dev->dev_private; NV_DEBUG_KMS(dev, "\n"); - pgpio = &dev_priv->engine.gpio; - if (pgpio->irq_unregister) { - pgpio->irq_unregister(dev, nv_connector->dcb->gpio_tag, - nouveau_connector_hotplug, connector); + if (nv_connector->hpd != DCB_GPIO_UNUSED) { + nouveau_gpio_isr_del(dev, 0, nv_connector->hpd, 0xff, + nouveau_connector_hotplug, connector); } kfree(nv_connector->edid); @@ -198,6 +180,10 @@ nouveau_connector_set_encoder(struct drm_connector *connector, return; nv_connector->detected_encoder = nv_encoder; + if (dev_priv->card_type >= NV_50) { + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + } else if (nv_encoder->dcb->type == OUTPUT_LVDS || nv_encoder->dcb->type == OUTPUT_TMDS) { connector->doublescan_allowed = false; @@ -214,7 +200,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector, connector->interlace_allowed = true; } - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { drm_connector_property_set_value(connector, dev->mode_config.dvi_i_subconnector_property, nv_encoder->dcb->type == OUTPUT_TMDS ? @@ -397,7 +383,7 @@ nouveau_connector_force(struct drm_connector *connector) struct nouveau_encoder *nv_encoder; int type; - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { if (connector->force == DRM_FORCE_ON_DIGITAL) type = OUTPUT_TMDS; else @@ -420,15 +406,21 @@ static int nouveau_connector_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { + struct drm_nouveau_private *dev_priv = connector->dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); struct drm_device *dev = connector->dev; + struct nouveau_crtc *nv_crtc; int ret; + nv_crtc = NULL; + if (connector->encoder && connector->encoder->crtc) + nv_crtc = nouveau_crtc(connector->encoder->crtc); + /* Scaling mode */ if (property == dev->mode_config.scaling_mode_property) { - struct nouveau_crtc *nv_crtc = NULL; bool modeset = false; switch (value) { @@ -454,8 +446,6 @@ nouveau_connector_set_property(struct drm_connector *connector, modeset = true; nv_connector->scaling_mode = value; - if (connector->encoder && connector->encoder->crtc) - nv_crtc = nouveau_crtc(connector->encoder->crtc); if (!nv_crtc) return 0; @@ -467,7 +457,7 @@ nouveau_connector_set_property(struct drm_connector *connector, if (!ret) return -EINVAL; } else { - ret = nv_crtc->set_scale(nv_crtc, value, true); + ret = nv_crtc->set_scale(nv_crtc, true); if (ret) return ret; } @@ -475,23 +465,58 @@ nouveau_connector_set_property(struct drm_connector *connector, return 0; } - /* Dithering */ - if (property == dev->mode_config.dithering_mode_property) { - struct nouveau_crtc *nv_crtc = NULL; + /* Underscan */ + if (property == disp->underscan_property) { + if (nv_connector->underscan != value) { + nv_connector->underscan = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; - if (value == DRM_MODE_DITHERING_ON) - nv_connector->use_dithering = true; - else - nv_connector->use_dithering = false; + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + if (property == disp->underscan_hborder_property) { + if (nv_connector->underscan_hborder != value) { + nv_connector->underscan_hborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + if (property == disp->underscan_vborder_property) { + if (nv_connector->underscan_vborder != value) { + nv_connector->underscan_vborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + /* Dithering */ + if (property == disp->dithering_mode) { + nv_connector->dithering_mode = value; + if (!nv_crtc || !nv_crtc->set_dither) + return 0; - if (connector->encoder && connector->encoder->crtc) - nv_crtc = nouveau_crtc(connector->encoder->crtc); + return nv_crtc->set_dither(nv_crtc, true); + } + if (property == disp->dithering_depth) { + nv_connector->dithering_depth = value; if (!nv_crtc || !nv_crtc->set_dither) return 0; - return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, - true); + return nv_crtc->set_dither(nv_crtc, true); } if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV) @@ -602,6 +627,46 @@ nouveau_connector_scaler_modes_add(struct drm_connector *connector) return modes; } +static void +nouveau_connector_detect_depth(struct drm_connector *connector) +{ + struct drm_nouveau_private *dev_priv = connector->dev->dev_private; + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct nvbios *bios = &dev_priv->vbios; + struct drm_display_mode *mode = nv_connector->native_mode; + bool duallink; + + /* if the edid is feeling nice enough to provide this info, use it */ + if (nv_connector->edid && connector->display_info.bpc) + return; + + /* if not, we're out of options unless we're LVDS, default to 6bpc */ + connector->display_info.bpc = 6; + if (nv_encoder->dcb->type != OUTPUT_LVDS) + return; + + /* LVDS: panel straps */ + if (bios->fp_no_ddc) { + if (bios->fp.if_is_24bit) + connector->display_info.bpc = 8; + return; + } + + /* LVDS: DDC panel, need to first determine the number of links to + * know which if_is_24bit flag to check... + */ + if (nv_connector->edid && + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) + duallink = ((u8 *)nv_connector->edid)[121] == 2; + else + duallink = mode->clock >= bios->fp.duallink_transition_clk; + + if ((!duallink && (bios->fp.strapless_is_24bit & 1)) || + ( duallink && (bios->fp.strapless_is_24bit & 2))) + connector->display_info.bpc = 8; +} + static int nouveau_connector_get_modes(struct drm_connector *connector) { @@ -631,6 +696,12 @@ nouveau_connector_get_modes(struct drm_connector *connector) nv_connector->native_mode = drm_mode_duplicate(dev, &mode); } + /* Determine display colour depth for everything except LVDS now, + * DP requires this before mode_valid() is called. + */ + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); + /* Find the native mode if this is a digital panel, if we didn't * find any modes through DDC previously add the native mode to * the list of modes. @@ -646,12 +717,19 @@ nouveau_connector_get_modes(struct drm_connector *connector) ret = 1; } + /* Determine LVDS colour depth, must happen after determining + * "native" mode as some VBIOS tables require us to use the + * pixel clock as part of the lookup... + */ + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); + if (nv_encoder->dcb->type == OUTPUT_TV) ret = get_slave_funcs(encoder)->get_modes(encoder, connector); - if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS || - nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG || - nv_connector->dcb->type == DCB_CONNECTOR_eDP) + if (nv_connector->type == DCB_CONNECTOR_LVDS || + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG || + nv_connector->type == DCB_CONNECTOR_eDP) ret += nouveau_connector_scaler_modes_add(connector); return ret; @@ -710,7 +788,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, case OUTPUT_DP: max_clock = nv_encoder->dp.link_nr; max_clock *= nv_encoder->dp.link_bw; - clock = clock * nouveau_connector_bpp(connector) / 10; + clock = clock * (connector->display_info.bpc * 3) / 10; break; default: BUG_ON(1); @@ -768,96 +846,175 @@ nouveau_connector_funcs_lvds = { .force = nouveau_connector_force }; +static int +drm_conntype_from_dcb(enum dcb_connector_type dcb) +{ + switch (dcb) { + case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA; + case DCB_CONNECTOR_TV_0 : + case DCB_CONNECTOR_TV_1 : + case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV; + case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII; + case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID; + case DCB_CONNECTOR_LVDS : + case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS; + case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort; + case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; + case DCB_CONNECTOR_HDMI_0 : + case DCB_CONNECTOR_HDMI_1 : return DRM_MODE_CONNECTOR_HDMIA; + default: + break; + } + + return DRM_MODE_CONNECTOR_Unknown; +} + struct drm_connector * nouveau_connector_create(struct drm_device *dev, int index) { const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct nouveau_display_engine *disp = &dev_priv->engine.display; struct nouveau_connector *nv_connector = NULL; - struct dcb_connector_table_entry *dcb = NULL; struct drm_connector *connector; int type, ret = 0; + bool dummy; NV_DEBUG_KMS(dev, "\n"); - if (index >= dev_priv->vbios.dcb.connector.entries) - return ERR_PTR(-EINVAL); - - dcb = &dev_priv->vbios.dcb.connector.entry[index]; - if (dcb->drm) - return dcb->drm; - - switch (dcb->type) { - case DCB_CONNECTOR_VGA: - type = DRM_MODE_CONNECTOR_VGA; - break; - case DCB_CONNECTOR_TV_0: - case DCB_CONNECTOR_TV_1: - case DCB_CONNECTOR_TV_3: - type = DRM_MODE_CONNECTOR_TV; - break; - case DCB_CONNECTOR_DVI_I: - type = DRM_MODE_CONNECTOR_DVII; - break; - case DCB_CONNECTOR_DVI_D: - type = DRM_MODE_CONNECTOR_DVID; - break; - case DCB_CONNECTOR_HDMI_0: - case DCB_CONNECTOR_HDMI_1: - type = DRM_MODE_CONNECTOR_HDMIA; - break; - case DCB_CONNECTOR_LVDS: - case DCB_CONNECTOR_LVDS_SPWG: - type = DRM_MODE_CONNECTOR_LVDS; - funcs = &nouveau_connector_funcs_lvds; - break; - case DCB_CONNECTOR_DP: - type = DRM_MODE_CONNECTOR_DisplayPort; - break; - case DCB_CONNECTOR_eDP: - type = DRM_MODE_CONNECTOR_eDP; - break; - default: - NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type); - return ERR_PTR(-EINVAL); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + nv_connector = nouveau_connector(connector); + if (nv_connector->index == index) + return connector; } nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); if (!nv_connector) return ERR_PTR(-ENOMEM); - nv_connector->dcb = dcb; + connector = &nv_connector->base; + nv_connector->index = index; + + /* attempt to parse vbios connector type and hotplug gpio */ + nv_connector->dcb = dcb_conn(dev, index); + if (nv_connector->dcb) { + static const u8 hpd[16] = { + 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60, + }; + + u32 entry = ROM16(nv_connector->dcb[0]); + if (dcb_conntab(dev)[3] >= 4) + entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; + + nv_connector->hpd = ffs((entry & 0x07033000) >> 12); + nv_connector->hpd = hpd[nv_connector->hpd]; + + nv_connector->type = nv_connector->dcb[0]; + if (drm_conntype_from_dcb(nv_connector->type) == + DRM_MODE_CONNECTOR_Unknown) { + NV_WARN(dev, "unknown connector type %02x\n", + nv_connector->type); + nv_connector->type = DCB_CONNECTOR_NONE; + } - /* defaults, will get overridden in detect() */ - connector->interlace_allowed = false; - connector->doublescan_allowed = false; + /* Gigabyte NX85T */ + if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } - drm_connector_init(dev, connector, funcs, type); - drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); + /* Gigabyte GV-NX86T512H */ + if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } + } else { + nv_connector->type = DCB_CONNECTOR_NONE; + nv_connector->hpd = DCB_GPIO_UNUSED; + } + + /* no vbios data, or an unknown dcb connector type - attempt to + * figure out something suitable ourselves + */ + if (nv_connector->type == DCB_CONNECTOR_NONE) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct dcb_table *dcbt = &dev_priv->vbios.dcb; + u32 encoders = 0; + int i; + + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector == nv_connector->index) + encoders |= (1 << dcbt->entry[i].type); + } - /* Check if we need dithering enabled */ - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { - bool dummy, is_24bit = false; + if (encoders & (1 << OUTPUT_DP)) { + if (encoders & (1 << OUTPUT_TMDS)) + nv_connector->type = DCB_CONNECTOR_DP; + else + nv_connector->type = DCB_CONNECTOR_eDP; + } else + if (encoders & (1 << OUTPUT_TMDS)) { + if (encoders & (1 << OUTPUT_ANALOG)) + nv_connector->type = DCB_CONNECTOR_DVI_I; + else + nv_connector->type = DCB_CONNECTOR_DVI_D; + } else + if (encoders & (1 << OUTPUT_ANALOG)) { + nv_connector->type = DCB_CONNECTOR_VGA; + } else + if (encoders & (1 << OUTPUT_LVDS)) { + nv_connector->type = DCB_CONNECTOR_LVDS; + } else + if (encoders & (1 << OUTPUT_TV)) { + nv_connector->type = DCB_CONNECTOR_TV_0; + } + } - ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit); + type = drm_conntype_from_dcb(nv_connector->type); + if (type == DRM_MODE_CONNECTOR_LVDS) { + ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); if (ret) { - NV_ERROR(dev, "Error parsing LVDS table, disabling " - "LVDS\n"); - goto fail; + NV_ERROR(dev, "Error parsing LVDS table, disabling\n"); + kfree(nv_connector); + return ERR_PTR(ret); } - nv_connector->use_dithering = !is_24bit; + funcs = &nouveau_connector_funcs_lvds; + } else { + funcs = &nouveau_connector_funcs; } + /* defaults, will get overridden in detect() */ + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + drm_connector_init(dev, connector, funcs, type); + drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); + /* Init DVI-I specific properties */ - if (dcb->type == DCB_CONNECTOR_DVI_I) { - drm_mode_create_dvi_i_properties(dev); + if (nv_connector->type == DCB_CONNECTOR_DVI_I) drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0); - drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); + + /* Add overscan compensation options to digital outputs */ + if (disp->underscan_property && + (nv_connector->type == DCB_CONNECTOR_DVI_D || + nv_connector->type == DCB_CONNECTOR_DVI_I || + nv_connector->type == DCB_CONNECTOR_HDMI_0 || + nv_connector->type == DCB_CONNECTOR_HDMI_1 || + nv_connector->type == DCB_CONNECTOR_DP)) { + drm_connector_attach_property(connector, + disp->underscan_property, + UNDERSCAN_OFF); + drm_connector_attach_property(connector, + disp->underscan_hborder_property, + 0); + drm_connector_attach_property(connector, + disp->underscan_vborder_property, + 0); } - switch (dcb->type) { + switch (nv_connector->type) { case DCB_CONNECTOR_VGA: if (dev_priv->card_type >= NV_50) { drm_connector_attach_property(connector, @@ -876,32 +1033,32 @@ nouveau_connector_create(struct drm_device *dev, int index) drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, nv_connector->scaling_mode); - drm_connector_attach_property(connector, - dev->mode_config.dithering_mode_property, - nv_connector->use_dithering ? - DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); + if (disp->dithering_mode) { + nv_connector->dithering_mode = DITHERING_MODE_AUTO; + drm_connector_attach_property(connector, + disp->dithering_mode, + nv_connector->dithering_mode); + } + if (disp->dithering_depth) { + nv_connector->dithering_depth = DITHERING_DEPTH_AUTO; + drm_connector_attach_property(connector, + disp->dithering_depth, + nv_connector->dithering_depth); + } break; } - if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) { - pgpio->irq_register(dev, nv_connector->dcb->gpio_tag, - nouveau_connector_hotplug, connector); - - connector->polled = DRM_CONNECTOR_POLL_HPD; - } else { - connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + if (nv_connector->hpd != DCB_GPIO_UNUSED) { + ret = nouveau_gpio_isr_add(dev, 0, nv_connector->hpd, 0xff, + nouveau_connector_hotplug, + connector); + if (ret == 0) + connector->polled = DRM_CONNECTOR_POLL_HPD; } drm_sysfs_connector_add(connector); - - dcb->drm = connector; - return dcb->drm; - -fail: - drm_connector_cleanup(connector); - kfree(connector); - return ERR_PTR(ret); - + return connector; } static void diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 711b1e9203af..e4857021304c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -30,13 +30,43 @@ #include "drm_edid.h" #include "nouveau_i2c.h" +enum nouveau_underscan_type { + UNDERSCAN_OFF, + UNDERSCAN_ON, + UNDERSCAN_AUTO, +}; + +/* the enum values specifically defined here match nv50/nvd0 hw values, and + * the code relies on this + */ +enum nouveau_dithering_mode { + DITHERING_MODE_OFF = 0x00, + DITHERING_MODE_ON = 0x01, + DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON, + DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON, + DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON, + DITHERING_MODE_AUTO +}; + +enum nouveau_dithering_depth { + DITHERING_DEPTH_6BPC = 0x00, + DITHERING_DEPTH_8BPC = 0x02, + DITHERING_DEPTH_AUTO +}; + struct nouveau_connector { struct drm_connector base; + enum dcb_connector_type type; + u8 index; + u8 *dcb; + u8 hpd; - struct dcb_connector_table_entry *dcb; - + int dithering_mode; + int dithering_depth; int scaling_mode; - bool use_dithering; + enum nouveau_underscan_type underscan; + u32 underscan_hborder; + u32 underscan_vborder; struct nouveau_encoder *detected_encoder; struct edid *edid; diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index bf8e1289953d..686f6b4a1da3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -32,8 +32,6 @@ struct nouveau_crtc { int index; - struct drm_display_mode *mode; - uint32_t dpms_saved_fp_control; uint32_t fp_users; int saturation; @@ -67,8 +65,8 @@ struct nouveau_crtc { int depth; } lut; - int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update); - int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update); + int (*set_dither)(struct nouveau_crtc *crtc, bool update); + int (*set_scale)(struct nouveau_crtc *crtc, bool update); }; static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index f52c2db3529e..fa2ec491f6a7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -44,7 +44,7 @@ nouveau_debugfs_channel_info(struct seq_file *m, void *data) seq_printf(m, "channel id : %d\n", chan->id); seq_printf(m, "cpu fifo state:\n"); - seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base); + seq_printf(m, " base: 0x%10llx\n", chan->pushbuf_base); seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2); seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2); seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2); diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 7e88cd7f2b99..3cb52bc52b21 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -32,6 +32,8 @@ #include "nouveau_hw.h" #include "nouveau_crtc.h" #include "nouveau_dma.h" +#include "nouveau_connector.h" +#include "nouveau_gpio.h" #include "nv50_display.h" static void @@ -147,11 +149,186 @@ nouveau_user_framebuffer_create(struct drm_device *dev, return &nouveau_fb->base; } -const struct drm_mode_config_funcs nouveau_mode_config_funcs = { +static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { .fb_create = nouveau_user_framebuffer_create, .output_poll_changed = nouveau_fbcon_output_poll_changed, }; + +struct drm_prop_enum_list { + u8 gen_mask; + int type; + char *name; +}; + +static struct drm_prop_enum_list underscan[] = { + { 6, UNDERSCAN_AUTO, "auto" }, + { 6, UNDERSCAN_OFF, "off" }, + { 6, UNDERSCAN_ON, "on" }, + {} +}; + +static struct drm_prop_enum_list dither_mode[] = { + { 7, DITHERING_MODE_AUTO, "auto" }, + { 7, DITHERING_MODE_OFF, "off" }, + { 1, DITHERING_MODE_ON, "on" }, + { 6, DITHERING_MODE_STATIC2X2, "static 2x2" }, + { 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" }, + { 4, DITHERING_MODE_TEMPORAL, "temporal" }, + {} +}; + +static struct drm_prop_enum_list dither_depth[] = { + { 6, DITHERING_DEPTH_AUTO, "auto" }, + { 6, DITHERING_DEPTH_6BPC, "6 bpc" }, + { 6, DITHERING_DEPTH_8BPC, "8 bpc" }, + {} +}; + +#define PROP_ENUM(p,gen,n,list) do { \ + struct drm_prop_enum_list *l = (list); \ + int c = 0; \ + while (l->gen_mask) { \ + if (l->gen_mask & (1 << (gen))) \ + c++; \ + l++; \ + } \ + if (c) { \ + p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c); \ + l = (list); \ + c = 0; \ + while (p && l->gen_mask) { \ + if (l->gen_mask & (1 << (gen))) { \ + drm_property_add_enum(p, c, l->type, l->name); \ + c++; \ + } \ + l++; \ + } \ + } \ +} while(0) + +int +nouveau_display_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct drm_connector *connector; + int ret; + + ret = disp->init(dev); + if (ret) + return ret; + + drm_kms_helper_poll_enable(dev); + + /* enable hotplug interrupts */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct nouveau_connector *conn = nouveau_connector(connector); + nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); + } + + return ret; +} + +void +nouveau_display_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct drm_connector *connector; + + /* disable hotplug interrupts */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct nouveau_connector *conn = nouveau_connector(connector); + nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, false); + } + + drm_kms_helper_poll_disable(dev); + disp->fini(dev); +} + +int +nouveau_display_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + int ret, gen; + + drm_mode_config_init(dev); + drm_mode_create_scaling_mode_property(dev); + drm_mode_create_dvi_i_properties(dev); + + if (dev_priv->card_type < NV_50) + gen = 0; + else + if (dev_priv->card_type < NV_D0) + gen = 1; + else + gen = 2; + + PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode); + PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth); + PROP_ENUM(disp->underscan_property, gen, "underscan", underscan); + + disp->underscan_hborder_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "underscan hborder", 2); + disp->underscan_hborder_property->values[0] = 0; + disp->underscan_hborder_property->values[1] = 128; + + disp->underscan_vborder_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "underscan vborder", 2); + disp->underscan_vborder_property->values[0] = 0; + disp->underscan_vborder_property->values[1] = 128; + + dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; + dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + if (dev_priv->card_type < NV_10) { + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + } else + if (dev_priv->card_type < NV_50) { + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + } else { + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + } + + drm_kms_helper_poll_init(dev); + drm_kms_helper_poll_disable(dev); + + ret = disp->create(dev); + if (ret) + return ret; + + if (dev->mode_config.num_crtc) { + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) + return ret; + } + + return ret; +} + +void +nouveau_display_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + + drm_vblank_cleanup(dev); + + disp->destroy(dev); + + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); +} + int nouveau_vblank_enable(struct drm_device *dev, int crtc) { @@ -305,7 +482,10 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Emit a page flip */ if (dev_priv->card_type >= NV_50) { - ret = nv50_display_flip_next(crtc, fb, chan); + if (dev_priv->card_type >= NV_D0) + ret = nvd0_display_flip_next(crtc, fb, chan, 0); + else + ret = nv50_display_flip_next(crtc, fb, chan); if (ret) { nouveau_channel_put(&chan); goto fail_unreserve; diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c index 00bc6eaad558..4c2e4e5925fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.c +++ b/drivers/gpu/drm/nouveau/nouveau_dma.c @@ -134,11 +134,13 @@ OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords) * -EBUSY if timeout exceeded */ static inline int -READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout) +READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout) { - uint32_t val; + uint64_t val; val = nvchan_rd32(chan, chan->user_get); + if (chan->user_get_hi) + val |= (uint64_t)nvchan_rd32(chan, chan->user_get_hi) << 32; /* reset counter as long as GET is still advancing, this is * to avoid misdetecting a GPU lockup if the GPU happens to @@ -218,8 +220,8 @@ nv50_dma_push_wait(struct nouveau_channel *chan, int count) static int nv50_dma_wait(struct nouveau_channel *chan, int slots, int count) { - uint32_t cnt = 0, prev_get = 0; - int ret; + uint64_t prev_get = 0; + int ret, cnt = 0; ret = nv50_dma_push_wait(chan, slots + 1); if (unlikely(ret)) @@ -261,8 +263,8 @@ nv50_dma_wait(struct nouveau_channel *chan, int slots, int count) int nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size) { - uint32_t prev_get = 0, cnt = 0; - int get; + uint64_t prev_get = 0; + int cnt = 0, get; if (chan->dma.ib_max) return nv50_dma_wait(chan, slots, size); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index de5efe71fefd..9b93b703ceab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -29,6 +29,7 @@ #include "nouveau_connector.h" #include "nouveau_encoder.h" #include "nouveau_crtc.h" +#include "nouveau_gpio.h" /****************************************************************************** * aux channel util functions @@ -273,8 +274,6 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) u8 * nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; struct bit_entry d; u8 *table; int i; @@ -289,7 +288,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) return NULL; } - table = ROMPTR(bios, d.data[0]); + table = ROMPTR(dev, d.data[0]); if (!table) { NV_ERROR(dev, "displayport table pointer invalid\n"); return NULL; @@ -306,7 +305,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) } for (i = 0; i < table[3]; i++) { - *entry = ROMPTR(bios, table[table[1] + (i * table[2])]); + *entry = ROMPTR(dev, table[table[1] + (i * table[2])]); if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0]))) return table; } @@ -336,7 +335,6 @@ struct dp_state { static void dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { - struct drm_nouveau_private *dev_priv = dev->dev_private; int or = dp->or, link = dp->link; u8 *entry, sink[2]; u32 dp_ctrl; @@ -360,7 +358,7 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) * table, that has (among other things) pointers to more scripts that * need to be executed, this time depending on link speed. */ - entry = ROMPTR(&dev_priv->vbios, dp->entry[10]); + entry = ROMPTR(dev, dp->entry[10]); if (entry) { if (dp->table[0] < 0x30) { while (dp->link_bw < (ROM16(entry[0]) * 10)) @@ -559,8 +557,6 @@ dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) bool nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) { - struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_connector *nv_connector = @@ -581,7 +577,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) dp.dcb = nv_encoder->dcb; dp.crtc = nv_crtc->index; - dp.auxch = auxch->rd; + dp.auxch = auxch->drive; dp.or = nv_encoder->or; dp.link = !(nv_encoder->dcb->sorconf.link & 1); dp.dpcd = nv_encoder->dp.dpcd; @@ -590,7 +586,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) * we take during link training (DP_SET_POWER is one), we need * to ignore them for the moment to avoid races. */ - pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); + nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, false); /* enable down-spreading, if possible */ if (dp.table[1] >= 16) { @@ -639,7 +635,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc); /* re-enable hotplug detect */ - pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); + nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, true); return true; } @@ -656,7 +652,7 @@ nouveau_dp_detect(struct drm_encoder *encoder) if (!auxch) return false; - ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8); + ret = auxch_tx(dev, auxch->drive, 9, DP_DPCD_REV, dpcd, 8); if (ret) return false; @@ -684,7 +680,7 @@ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr) { - return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr); + return auxch_tx(auxch->dev, auxch->drive, cmd, addr, data, data_nr); } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index f0a60afac446..e4a7cfe7898d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -124,6 +124,10 @@ MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n"); int nouveau_ctxfw; module_param_named(ctxfw, nouveau_ctxfw, int, 0400); +MODULE_PARM_DESC(ctxfw, "Santise DCB table according to MXM-SIS\n"); +int nouveau_mxmdcb = 1; +module_param_named(mxmdcb, nouveau_mxmdcb, int, 0400); + int nouveau_fbpercrtc; #if 0 module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400); @@ -178,8 +182,11 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - NV_INFO(dev, "Disabling fbcon acceleration...\n"); - nouveau_fbcon_save_disable_accel(dev); + NV_INFO(dev, "Disabling display...\n"); + nouveau_display_fini(dev); + + NV_INFO(dev, "Disabling fbcon...\n"); + nouveau_fbcon_set_suspend(dev, 1); NV_INFO(dev, "Unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -220,7 +227,7 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) ret = dev_priv->eng[e]->fini(dev, e, true); if (ret) { - NV_ERROR(dev, "... engine %d failed: %d\n", i, ret); + NV_ERROR(dev, "... engine %d failed: %d\n", e, ret); goto out_abort; } } @@ -246,10 +253,6 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) pci_set_power_state(pdev, PCI_D3hot); } - console_lock(); - nouveau_fbcon_set_suspend(dev, 1); - console_unlock(); - nouveau_fbcon_restore_accel(dev); return 0; out_abort: @@ -275,8 +278,6 @@ nouveau_pci_resume(struct pci_dev *pdev) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - nouveau_fbcon_save_disable_accel(dev); - NV_INFO(dev, "We're back, enabling device...\n"); pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); @@ -296,8 +297,6 @@ nouveau_pci_resume(struct pci_dev *pdev) if (ret) return ret; - nouveau_pm_resume(dev); - if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) { ret = nouveau_mem_init_agp(dev); if (ret) { @@ -337,6 +336,8 @@ nouveau_pci_resume(struct pci_dev *pdev) } } + nouveau_pm_resume(dev); + NV_INFO(dev, "Restoring mode...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -358,16 +359,10 @@ nouveau_pci_resume(struct pci_dev *pdev) NV_ERROR(dev, "Could not pin/map cursor.\n"); } - engine->display.init(dev); - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 offset = nv_crtc->cursor.nvbo->bo.offset; + nouveau_fbcon_set_suspend(dev, 0); + nouveau_fbcon_zfill_all(dev); - nv_crtc->cursor.set_offset(nv_crtc, offset); - nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, - nv_crtc->cursor_saved_y); - } + nouveau_display_init(dev); /* Force CLUT to get re-loaded during modeset */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -376,15 +371,17 @@ nouveau_pci_resume(struct pci_dev *pdev) nv_crtc->lut.depth = 0; } - console_lock(); - nouveau_fbcon_set_suspend(dev, 0); - console_unlock(); + drm_helper_resume_force_mode(dev); - nouveau_fbcon_zfill_all(dev); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 offset = nv_crtc->cursor.nvbo->bo.offset; - drm_helper_resume_force_mode(dev); + nv_crtc->cursor.set_offset(nv_crtc, offset); + nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, + nv_crtc->cursor_saved_y); + } - nouveau_fbcon_restore_accel(dev); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index dfddb7e078a1..38134a9c7578 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -163,6 +163,9 @@ enum nouveau_flags { #define NVOBJ_ENGINE_COPY0 3 #define NVOBJ_ENGINE_COPY1 4 #define NVOBJ_ENGINE_MPEG 5 +#define NVOBJ_ENGINE_PPP NVOBJ_ENGINE_MPEG +#define NVOBJ_ENGINE_BSP 6 +#define NVOBJ_ENGINE_VP 7 #define NVOBJ_ENGINE_DISPLAY 15 #define NVOBJ_ENGINE_NR 16 @@ -229,6 +232,7 @@ struct nouveau_channel { /* mapping of the regs controlling the fifo */ void __iomem *user; uint32_t user_get; + uint32_t user_get_hi; uint32_t user_put; /* Fencing */ @@ -246,7 +250,7 @@ struct nouveau_channel { struct nouveau_gpuobj *pushbuf; struct nouveau_bo *pushbuf_bo; struct nouveau_vma pushbuf_vma; - uint32_t pushbuf_base; + uint64_t pushbuf_base; /* Notifier memory */ struct nouveau_bo *notifier_bo; @@ -393,24 +397,25 @@ struct nouveau_display_engine { int (*early_init)(struct drm_device *); void (*late_takedown)(struct drm_device *); int (*create)(struct drm_device *); - int (*init)(struct drm_device *); void (*destroy)(struct drm_device *); + int (*init)(struct drm_device *); + void (*fini)(struct drm_device *); + + struct drm_property *dithering_mode; + struct drm_property *dithering_depth; + struct drm_property *underscan_property; + struct drm_property *underscan_hborder_property; + struct drm_property *underscan_vborder_property; }; struct nouveau_gpio_engine { - void *priv; - - int (*init)(struct drm_device *); - void (*takedown)(struct drm_device *); - - int (*get)(struct drm_device *, enum dcb_gpio_tag); - int (*set)(struct drm_device *, enum dcb_gpio_tag, int state); - - int (*irq_register)(struct drm_device *, enum dcb_gpio_tag, - void (*)(void *, int), void *); - void (*irq_unregister)(struct drm_device *, enum dcb_gpio_tag, - void (*)(void *, int), void *); - bool (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on); + spinlock_t lock; + struct list_head isr; + int (*init)(struct drm_device *); + void (*fini)(struct drm_device *); + int (*drive)(struct drm_device *, int line, int dir, int out); + int (*sense)(struct drm_device *, int line); + void (*irq_enable)(struct drm_device *, int line, bool); }; struct nouveau_pm_voltage_level { @@ -484,7 +489,7 @@ struct nouveau_pm_level { u32 copy; u32 daemon; u32 vdec; - u32 unk05; /* nv50:nva3, roughly.. */ + u32 dom6; u32 unka0; /* nva3:nvc0 */ u32 hub01; /* nvc0- */ u32 hub06; /* nvc0- */ @@ -518,6 +523,12 @@ struct nouveau_pm_memtimings { int nr_timing; }; +struct nouveau_pm_fan { + u32 min_duty; + u32 max_duty; + u32 pwm_freq; +}; + struct nouveau_pm_engine { struct nouveau_pm_voltage voltage; struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; @@ -525,6 +536,8 @@ struct nouveau_pm_engine { struct nouveau_pm_memtimings memtimings; struct nouveau_pm_temp_sensor_constants sensor_constants; struct nouveau_pm_threshold_temp threshold_temp; + struct nouveau_pm_fan fan; + u32 pwm_divisor; struct nouveau_pm_level boot; struct nouveau_pm_level *cur; @@ -532,19 +545,14 @@ struct nouveau_pm_engine { struct device *hwmon; struct notifier_block acpi_nb; - int (*clock_get)(struct drm_device *, u32 id); - void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *, - u32 id, int khz); - void (*clock_set)(struct drm_device *, void *); - int (*clocks_get)(struct drm_device *, struct nouveau_pm_level *); void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *); - void (*clocks_set)(struct drm_device *, void *); + int (*clocks_set)(struct drm_device *, void *); int (*voltage_get)(struct drm_device *); int (*voltage_set)(struct drm_device *, int voltage); - int (*fanspeed_get)(struct drm_device *); - int (*fanspeed_set)(struct drm_device *, int fanspeed); + int (*pwm_get)(struct drm_device *, int line, u32*, u32*); + int (*pwm_set)(struct drm_device *, int line, u32, u32); int (*temp_get)(struct drm_device *); }; @@ -780,6 +788,8 @@ struct drm_nouveau_private { struct nouveau_vm *chan_vm; struct nvbios vbios; + u8 *mxms; + struct list_head i2c_ports; struct nv04_mode_state mode_reg; struct nv04_mode_state saved_reg; @@ -850,6 +860,7 @@ extern char *nouveau_perflvl; extern int nouveau_perflvl_wr; extern int nouveau_msi; extern int nouveau_ctxfw; +extern int nouveau_mxmdcb; extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); extern int nouveau_pci_resume(struct pci_dev *pdev); @@ -1075,8 +1086,6 @@ extern int nouveau_run_vbios_init(struct drm_device *); extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table, struct dcb_entry *, int crtc); extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table); -extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *, - enum dcb_gpio_tag); extern struct dcb_connector_table_entry * nouveau_bios_connector_entry(struct drm_device *, int index); extern u32 get_pll_register(struct drm_device *, enum pll_types); @@ -1094,11 +1103,18 @@ extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head, enum LVDS_script, int pxclk); bool bios_encoder_match(struct dcb_entry *, u32 hash); +/* nouveau_mxm.c */ +int nouveau_mxm_init(struct drm_device *dev); +void nouveau_mxm_fini(struct drm_device *dev); + /* nouveau_ttm.c */ int nouveau_ttm_global_init(struct drm_nouveau_private *); void nouveau_ttm_global_release(struct drm_nouveau_private *); int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); +/* nouveau_hdmi.c */ +void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); + /* nouveau_dp.c */ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); @@ -1225,6 +1241,9 @@ extern int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst); /* nv84_crypt.c */ extern int nv84_crypt_create(struct drm_device *); +/* nv98_crypt.c */ +extern int nv98_crypt_create(struct drm_device *dev); + /* nva3_copy.c */ extern int nva3_copy_create(struct drm_device *dev); @@ -1237,6 +1256,17 @@ extern int nv31_mpeg_create(struct drm_device *dev); /* nv50_mpeg.c */ extern int nv50_mpeg_create(struct drm_device *dev); +/* nv84_bsp.c */ +/* nv98_bsp.c */ +extern int nv84_bsp_create(struct drm_device *dev); + +/* nv84_vp.c */ +/* nv98_vp.c */ +extern int nv84_vp_create(struct drm_device *dev); + +/* nv98_ppp.c */ +extern int nv98_ppp_create(struct drm_device *dev); + /* nv04_instmem.c */ extern int nv04_instmem_init(struct drm_device *); extern void nv04_instmem_takedown(struct drm_device *); @@ -1314,13 +1344,19 @@ extern int nv17_tv_create(struct drm_connector *, struct dcb_entry *); extern int nv04_display_early_init(struct drm_device *); extern void nv04_display_late_takedown(struct drm_device *); extern int nv04_display_create(struct drm_device *); -extern int nv04_display_init(struct drm_device *); extern void nv04_display_destroy(struct drm_device *); +extern int nv04_display_init(struct drm_device *); +extern void nv04_display_fini(struct drm_device *); /* nvd0_display.c */ extern int nvd0_display_create(struct drm_device *); -extern int nvd0_display_init(struct drm_device *); extern void nvd0_display_destroy(struct drm_device *); +extern int nvd0_display_init(struct drm_device *); +extern void nvd0_display_fini(struct drm_device *); +struct nouveau_bo *nvd0_display_crtc_sema(struct drm_device *, int crtc); +void nvd0_display_flip_stop(struct drm_crtc *); +int nvd0_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, + struct nouveau_channel *, u32 swap_interval); /* nv04_crtc.c */ extern int nv04_crtc_create(struct drm_device *, int index); @@ -1415,6 +1451,10 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); /* nouveau_display.c */ +int nouveau_display_create(struct drm_device *dev); +void nouveau_display_destroy(struct drm_device *dev); +int nouveau_display_init(struct drm_device *dev); +void nouveau_display_fini(struct drm_device *dev); int nouveau_vblank_enable(struct drm_device *dev, int crtc); void nouveau_vblank_disable(struct drm_device *dev, int crtc); int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -1429,23 +1469,22 @@ int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *, uint32_t handle); /* nv10_gpio.c */ -int nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); -int nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); +int nv10_gpio_init(struct drm_device *dev); +void nv10_gpio_fini(struct drm_device *dev); +int nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out); +int nv10_gpio_sense(struct drm_device *dev, int line); +void nv10_gpio_irq_enable(struct drm_device *, int line, bool on); /* nv50_gpio.c */ int nv50_gpio_init(struct drm_device *dev); void nv50_gpio_fini(struct drm_device *dev); -int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); -int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); -int nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); -int nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); -int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag, - void (*)(void *, int), void *); -void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag, - void (*)(void *, int), void *); -bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on); - -/* nv50_calc. */ +int nv50_gpio_drive(struct drm_device *dev, int line, int dir, int out); +int nv50_gpio_sense(struct drm_device *dev, int line); +void nv50_gpio_irq_enable(struct drm_device *, int line, bool on); +int nvd0_gpio_drive(struct drm_device *dev, int line, int dir, int out); +int nvd0_gpio_sense(struct drm_device *dev, int line); + +/* nv50_calc.c */ int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, int *N1, int *M1, int *N2, int *M2, int *P); int nva3_calc_pll(struct drm_device *, struct pll_lims *, @@ -1568,6 +1607,13 @@ extern void nv_wo32(struct nouveau_gpuobj *, u32 offset, u32 val); #define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg) #define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg) #define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg) +#define NV_WARNONCE(d, fmt, arg...) do { \ + static int _warned = 0; \ + if (!_warned) { \ + NV_WARN(d, fmt, ##arg); \ + _warned = 1; \ + } \ +} while(0) /* nouveau_reg_debug bitmask */ enum { diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h index f4dd30150879..f3fb649fe454 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fb.h +++ b/drivers/gpu/drm/nouveau/nouveau_fb.h @@ -42,8 +42,6 @@ nouveau_framebuffer(struct drm_framebuffer *fb) return container_of(fb, struct nouveau_framebuffer, base); } -extern const struct drm_mode_config_funcs nouveau_mode_config_funcs; - int nouveau_framebuffer_init(struct drm_device *dev, struct nouveau_framebuffer *nouveau_fb, struct drm_mode_fb_cmd2 *mode_cmd, struct nouveau_bo *nvbo); #endif /* __NOUVEAU_FB_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index dbb151834121..9892218d7452 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -36,6 +36,7 @@ #include <linux/init.h> #include <linux/screen_info.h> #include <linux/vga_switcheroo.h> +#include <linux/console.h> #include "drmP.h" #include "drm.h" @@ -548,7 +549,13 @@ void nouveau_fbcon_restore_accel(struct drm_device *dev) void nouveau_fbcon_set_suspend(struct drm_device *dev, int state) { struct drm_nouveau_private *dev_priv = dev->dev_private; + console_lock(); + if (state == 0) + nouveau_fbcon_save_disable_accel(dev); fb_set_suspend(dev_priv->nfbdev->helper.fbdev, state); + if (state == 1) + nouveau_fbcon_restore_accel(dev); + console_unlock(); } void nouveau_fbcon_zfill_all(struct drm_device *dev) diff --git a/drivers/gpu/drm/nouveau/nouveau_gpio.c b/drivers/gpu/drm/nouveau/nouveau_gpio.c new file mode 100644 index 000000000000..a580cc62337a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_gpio.c @@ -0,0 +1,400 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_i2c.h" +#include "nouveau_gpio.h" + +static u8 * +dcb_gpio_table(struct drm_device *dev) +{ + u8 *dcb = dcb_table(dev); + if (dcb) { + if (dcb[0] >= 0x30 && dcb[1] >= 0x0c) + return ROMPTR(dev, dcb[0x0a]); + if (dcb[0] >= 0x22 && dcb[-1] >= 0x13) + return ROMPTR(dev, dcb[-15]); + } + return NULL; +} + +static u8 * +dcb_gpio_entry(struct drm_device *dev, int idx, int ent, u8 *version) +{ + u8 *table = dcb_gpio_table(dev); + if (table) { + *version = table[0]; + if (*version < 0x30 && ent < table[2]) + return table + 3 + (ent * table[1]); + else if (ent < table[2]) + return table + table[1] + (ent * table[3]); + } + return NULL; +} + +int +nouveau_gpio_drive(struct drm_device *dev, int idx, int line, int dir, int out) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + return pgpio->drive ? pgpio->drive(dev, line, dir, out) : -ENODEV; +} + +int +nouveau_gpio_sense(struct drm_device *dev, int idx, int line) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + return pgpio->sense ? pgpio->sense(dev, line) : -ENODEV; +} + +int +nouveau_gpio_find(struct drm_device *dev, int idx, u8 func, u8 line, + struct gpio_func *gpio) +{ + u8 *table, *entry, version; + int i = -1; + + if (line == 0xff && func == 0xff) + return -EINVAL; + + while ((entry = dcb_gpio_entry(dev, idx, ++i, &version))) { + if (version < 0x40) { + u16 data = ROM16(entry[0]); + *gpio = (struct gpio_func) { + .line = (data & 0x001f) >> 0, + .func = (data & 0x07e0) >> 5, + .log[0] = (data & 0x1800) >> 11, + .log[1] = (data & 0x6000) >> 13, + }; + } else + if (version < 0x41) { + *gpio = (struct gpio_func) { + .line = entry[0] & 0x1f, + .func = entry[1], + .log[0] = (entry[3] & 0x18) >> 3, + .log[1] = (entry[3] & 0x60) >> 5, + }; + } else { + *gpio = (struct gpio_func) { + .line = entry[0] & 0x3f, + .func = entry[1], + .log[0] = (entry[4] & 0x30) >> 4, + .log[1] = (entry[4] & 0xc0) >> 6, + }; + } + + if ((line == 0xff || line == gpio->line) && + (func == 0xff || func == gpio->func)) + return 0; + } + + /* DCB 2.2, fixed TVDAC GPIO data */ + if ((table = dcb_table(dev)) && table[0] >= 0x22) { + if (func == DCB_GPIO_TVDAC0) { + *gpio = (struct gpio_func) { + .func = DCB_GPIO_TVDAC0, + .line = table[-4] >> 4, + .log[0] = !!(table[-5] & 2), + .log[1] = !(table[-5] & 2), + }; + return 0; + } + } + + /* Apple iMac G4 NV18 */ + if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) { + if (func == DCB_GPIO_TVDAC0) { + *gpio = (struct gpio_func) { + .func = DCB_GPIO_TVDAC0, + .line = 4, + .log[0] = 0, + .log[1] = 1, + }; + return 0; + } + } + + return -EINVAL; +} + +int +nouveau_gpio_set(struct drm_device *dev, int idx, u8 tag, u8 line, int state) +{ + struct gpio_func gpio; + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); + if (ret == 0) { + int dir = !!(gpio.log[state] & 0x02); + int out = !!(gpio.log[state] & 0x01); + ret = nouveau_gpio_drive(dev, idx, gpio.line, dir, out); + } + + return ret; +} + +int +nouveau_gpio_get(struct drm_device *dev, int idx, u8 tag, u8 line) +{ + struct gpio_func gpio; + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); + if (ret == 0) { + ret = nouveau_gpio_sense(dev, idx, gpio.line); + if (ret >= 0) + ret = (ret == (gpio.log[1] & 1)); + } + + return ret; +} + +int +nouveau_gpio_irq(struct drm_device *dev, int idx, u8 tag, u8 line, bool on) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_func gpio; + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); + if (ret == 0) { + if (idx == 0 && pgpio->irq_enable) + pgpio->irq_enable(dev, gpio.line, on); + else + ret = -ENODEV; + } + + return ret; +} + +struct gpio_isr { + struct drm_device *dev; + struct list_head head; + struct work_struct work; + int idx; + struct gpio_func func; + void (*handler)(void *, int); + void *data; + bool inhibit; +}; + +static void +nouveau_gpio_isr_bh(struct work_struct *work) +{ + struct gpio_isr *isr = container_of(work, struct gpio_isr, work); + struct drm_device *dev = isr->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + unsigned long flags; + int state; + + state = nouveau_gpio_get(dev, isr->idx, isr->func.func, isr->func.line); + if (state >= 0) + isr->handler(isr->data, state); + + spin_lock_irqsave(&pgpio->lock, flags); + isr->inhibit = false; + spin_unlock_irqrestore(&pgpio->lock, flags); +} + +void +nouveau_gpio_isr(struct drm_device *dev, int idx, u32 line_mask) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_isr *isr; + + if (idx != 0) + return; + + spin_lock(&pgpio->lock); + list_for_each_entry(isr, &pgpio->isr, head) { + if (line_mask & (1 << isr->func.line)) { + if (isr->inhibit) + continue; + isr->inhibit = true; + schedule_work(&isr->work); + } + } + spin_unlock(&pgpio->lock); +} + +int +nouveau_gpio_isr_add(struct drm_device *dev, int idx, u8 tag, u8 line, + void (*handler)(void *, int), void *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_isr *isr; + unsigned long flags; + int ret; + + isr = kzalloc(sizeof(*isr), GFP_KERNEL); + if (!isr) + return -ENOMEM; + + ret = nouveau_gpio_find(dev, idx, tag, line, &isr->func); + if (ret) { + kfree(isr); + return ret; + } + + INIT_WORK(&isr->work, nouveau_gpio_isr_bh); + isr->dev = dev; + isr->handler = handler; + isr->data = data; + isr->idx = idx; + + spin_lock_irqsave(&pgpio->lock, flags); + list_add(&isr->head, &pgpio->isr); + spin_unlock_irqrestore(&pgpio->lock, flags); + return 0; +} + +void +nouveau_gpio_isr_del(struct drm_device *dev, int idx, u8 tag, u8 line, + void (*handler)(void *, int), void *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_isr *isr, *tmp; + struct gpio_func func; + unsigned long flags; + LIST_HEAD(tofree); + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &func); + if (ret == 0) { + spin_lock_irqsave(&pgpio->lock, flags); + list_for_each_entry_safe(isr, tmp, &pgpio->isr, head) { + if (memcmp(&isr->func, &func, sizeof(func)) || + isr->idx != idx || + isr->handler != handler || isr->data != data) + continue; + list_move(&isr->head, &tofree); + } + spin_unlock_irqrestore(&pgpio->lock, flags); + + list_for_each_entry_safe(isr, tmp, &tofree, head) { + flush_work_sync(&isr->work); + kfree(isr); + } + } +} + +int +nouveau_gpio_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + INIT_LIST_HEAD(&pgpio->isr); + spin_lock_init(&pgpio->lock); + + return nouveau_gpio_init(dev); +} + +void +nouveau_gpio_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + nouveau_gpio_fini(dev); + BUG_ON(!list_empty(&pgpio->isr)); +} + +int +nouveau_gpio_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + int ret = 0; + + if (pgpio->init) + ret = pgpio->init(dev); + + return ret; +} + +void +nouveau_gpio_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + if (pgpio->fini) + pgpio->fini(dev); +} + +void +nouveau_gpio_reset(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u8 *entry, version; + int ent = -1; + + while ((entry = dcb_gpio_entry(dev, 0, ++ent, &version))) { + u8 func = 0xff, line, defs, unk0, unk1; + if (version >= 0x41) { + defs = !!(entry[0] & 0x80); + line = entry[0] & 0x3f; + func = entry[1]; + unk0 = entry[2]; + unk1 = entry[3] & 0x1f; + } else + if (version >= 0x40) { + line = entry[0] & 0x1f; + func = entry[1]; + defs = !!(entry[3] & 0x01); + unk0 = !!(entry[3] & 0x02); + unk1 = !!(entry[3] & 0x04); + } else { + break; + } + + if (func == 0xff) + continue; + + nouveau_gpio_func_set(dev, func, defs); + + if (dev_priv->card_type >= NV_D0) { + nv_mask(dev, 0x00d610 + (line * 4), 0xff, unk0); + if (unk1--) + nv_mask(dev, 0x00d640 + (unk1 * 4), 0xff, line); + } else + if (dev_priv->card_type >= NV_50) { + static const u32 regs[] = { 0xe100, 0xe28c }; + u32 val = (unk1 << 16) | unk0; + u32 reg = regs[line >> 4]; line &= 0x0f; + + nv_mask(dev, reg, 0x00010001 << line, val << line); + } + } +} diff --git a/drivers/gpu/drm/nouveau/nouveau_gpio.h b/drivers/gpu/drm/nouveau/nouveau_gpio.h new file mode 100644 index 000000000000..64c5cb077ace --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_gpio.h @@ -0,0 +1,71 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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_GPIO_H__ +#define __NOUVEAU_GPIO_H__ + +struct gpio_func { + u8 func; + u8 line; + u8 log[2]; +}; + +/* nouveau_gpio.c */ +int nouveau_gpio_create(struct drm_device *); +void nouveau_gpio_destroy(struct drm_device *); +int nouveau_gpio_init(struct drm_device *); +void nouveau_gpio_fini(struct drm_device *); +void nouveau_gpio_reset(struct drm_device *); +int nouveau_gpio_drive(struct drm_device *, int idx, int line, + int dir, int out); +int nouveau_gpio_sense(struct drm_device *, int idx, int line); +int nouveau_gpio_find(struct drm_device *, int idx, u8 tag, u8 line, + struct gpio_func *); +int nouveau_gpio_set(struct drm_device *, int idx, u8 tag, u8 line, int state); +int nouveau_gpio_get(struct drm_device *, int idx, u8 tag, u8 line); +int nouveau_gpio_irq(struct drm_device *, int idx, u8 tag, u8 line, bool on); +void nouveau_gpio_isr(struct drm_device *, int idx, u32 mask); +int nouveau_gpio_isr_add(struct drm_device *, int idx, u8 tag, u8 line, + void (*)(void *, int state), void *data); +void nouveau_gpio_isr_del(struct drm_device *, int idx, u8 tag, u8 line, + void (*)(void *, int state), void *data); + +static inline bool +nouveau_gpio_func_valid(struct drm_device *dev, u8 tag) +{ + struct gpio_func func; + return (nouveau_gpio_find(dev, 0, tag, 0xff, &func)) == 0; +} + +static inline int +nouveau_gpio_func_set(struct drm_device *dev, u8 tag, int state) +{ + return nouveau_gpio_set(dev, 0, tag, 0xff, state); +} + +static inline int +nouveau_gpio_func_get(struct drm_device *dev, u8 tag) +{ + return nouveau_gpio_get(dev, 0, tag, 0xff); +} + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c new file mode 100644 index 000000000000..59ea1c14eca0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c @@ -0,0 +1,258 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_connector.h" +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" + +static bool +hdmi_sor(struct drm_encoder *encoder) +{ + struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; + if (dev_priv->chipset < 0xa3) + return false; + return true; +} + +static inline u32 +hdmi_base(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + if (!hdmi_sor(encoder)) + return 0x616500 + (nv_crtc->index * 0x800); + return 0x61c500 + (nv_encoder->or * 0x800); +} + +static void +hdmi_wr32(struct drm_encoder *encoder, u32 reg, u32 val) +{ + nv_wr32(encoder->dev, hdmi_base(encoder) + reg, val); +} + +static u32 +hdmi_rd32(struct drm_encoder *encoder, u32 reg) +{ + return nv_rd32(encoder->dev, hdmi_base(encoder) + reg); +} + +static u32 +hdmi_mask(struct drm_encoder *encoder, u32 reg, u32 mask, u32 val) +{ + u32 tmp = hdmi_rd32(encoder, reg); + hdmi_wr32(encoder, reg, (tmp & ~mask) | val); + return tmp; +} + +static void +nouveau_audio_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + u32 or = nv_encoder->or * 0x800; + + if (hdmi_sor(encoder)) { + nv_mask(dev, 0x61c448 + or, 0x00000003, 0x00000000); + } +} + +static void +nouveau_audio_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + u32 or = nv_encoder->or * 0x800; + int i; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_monitor_audio(nv_connector->edid)) { + nouveau_audio_disconnect(encoder); + return; + } + + if (hdmi_sor(encoder)) { + nv_mask(dev, 0x61c448 + or, 0x00000001, 0x00000001); + + drm_edid_to_eld(&nv_connector->base, nv_connector->edid); + if (nv_connector->base.eld[0]) { + u8 *eld = nv_connector->base.eld; + for (i = 0; i < eld[2] * 4; i++) + nv_wr32(dev, 0x61c440 + or, (i << 8) | eld[i]); + for (i = eld[2] * 4; i < 0x60; i++) + nv_wr32(dev, 0x61c440 + or, (i << 8) | 0x00); + nv_mask(dev, 0x61c448 + or, 0x00000002, 0x00000002); + } + } +} + +static void +nouveau_hdmi_infoframe(struct drm_encoder *encoder, u32 ctrl, u8 *frame) +{ + /* calculate checksum for the infoframe */ + u8 sum = 0, i; + for (i = 0; i < frame[2]; i++) + sum += frame[i]; + frame[3] = 256 - sum; + + /* disable infoframe, and write header */ + hdmi_mask(encoder, ctrl + 0x00, 0x00000001, 0x00000000); + hdmi_wr32(encoder, ctrl + 0x08, *(u32 *)frame & 0xffffff); + + /* register scans tell me the audio infoframe has only one set of + * subpack regs, according to tegra (gee nvidia, it'd be nice if we + * could get those docs too!), the hdmi block pads out the rest of + * the packet on its own. + */ + if (ctrl == 0x020) + frame[2] = 6; + + /* write out checksum and data, weird weird 7 byte register pairs */ + for (i = 0; i < frame[2] + 1; i += 7) { + u32 rsubpack = ctrl + 0x0c + ((i / 7) * 8); + u32 *subpack = (u32 *)&frame[3 + i]; + hdmi_wr32(encoder, rsubpack + 0, subpack[0]); + hdmi_wr32(encoder, rsubpack + 4, subpack[1] & 0xffffff); + } + + /* enable the infoframe */ + hdmi_mask(encoder, ctrl, 0x00000001, 0x00000001); +} + +static void +nouveau_hdmi_video_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + const u8 Y = 0, A = 0, B = 0, S = 0, C = 0, M = 0, R = 0; + const u8 ITC = 0, EC = 0, Q = 0, SC = 0, VIC = 0, PR = 0; + const u8 bar_top = 0, bar_bottom = 0, bar_left = 0, bar_right = 0; + u8 frame[20]; + + frame[0x00] = 0x82; /* AVI infoframe */ + frame[0x01] = 0x02; /* version */ + frame[0x02] = 0x0d; /* length */ + frame[0x03] = 0x00; + frame[0x04] = (Y << 5) | (A << 4) | (B << 2) | S; + frame[0x05] = (C << 6) | (M << 4) | R; + frame[0x06] = (ITC << 7) | (EC << 4) | (Q << 2) | SC; + frame[0x07] = VIC; + frame[0x08] = PR; + frame[0x09] = bar_top & 0xff; + frame[0x0a] = bar_top >> 8; + frame[0x0b] = bar_bottom & 0xff; + frame[0x0c] = bar_bottom >> 8; + frame[0x0d] = bar_left & 0xff; + frame[0x0e] = bar_left >> 8; + frame[0x0f] = bar_right & 0xff; + frame[0x10] = bar_right >> 8; + frame[0x11] = 0x00; + frame[0x12] = 0x00; + frame[0x13] = 0x00; + + nouveau_hdmi_infoframe(encoder, 0x020, frame); +} + +static void +nouveau_hdmi_audio_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + const u8 CT = 0x00, CC = 0x01, ceaSS = 0x00, SF = 0x00, FMT = 0x00; + const u8 CA = 0x00, DM_INH = 0, LSV = 0x00; + u8 frame[12]; + + frame[0x00] = 0x84; /* Audio infoframe */ + frame[0x01] = 0x01; /* version */ + frame[0x02] = 0x0a; /* length */ + frame[0x03] = 0x00; + frame[0x04] = (CT << 4) | CC; + frame[0x05] = (SF << 2) | ceaSS; + frame[0x06] = FMT; + frame[0x07] = CA; + frame[0x08] = (DM_INH << 7) | (LSV << 3); + frame[0x09] = 0x00; + frame[0x0a] = 0x00; + frame[0x0b] = 0x00; + + nouveau_hdmi_infoframe(encoder, 0x000, frame); +} + +static void +nouveau_hdmi_disconnect(struct drm_encoder *encoder) +{ + nouveau_audio_disconnect(encoder); + + /* disable audio and avi infoframes */ + hdmi_mask(encoder, 0x000, 0x00000001, 0x00000000); + hdmi_mask(encoder, 0x020, 0x00000001, 0x00000000); + + /* disable hdmi */ + hdmi_mask(encoder, 0x0a4, 0x40000000, 0x00000000); +} + +void +nouveau_hdmi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + u32 max_ac_packet, rekey; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!mode || !nv_connector || !nv_connector->edid || + !drm_detect_hdmi_monitor(nv_connector->edid)) { + nouveau_hdmi_disconnect(encoder); + return; + } + + nouveau_hdmi_video_infoframe(encoder, mode); + nouveau_hdmi_audio_infoframe(encoder, mode); + + hdmi_mask(encoder, 0x0d0, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ + hdmi_mask(encoder, 0x068, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ + hdmi_mask(encoder, 0x078, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ + + nv_mask(dev, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nv_mask(dev, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nv_mask(dev, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* value matches nvidia binary driver, and tegra constant */ + rekey = 56; + + max_ac_packet = mode->htotal - mode->hdisplay; + max_ac_packet -= rekey; + max_ac_packet -= 18; /* constant from tegra */ + max_ac_packet /= 32; + + /* enable hdmi */ + hdmi_mask(encoder, 0x0a4, 0x5f1f003f, 0x40000000 | /* enable */ + 0x1f000000 | /* unknown */ + max_ac_packet << 16 | + rekey); + + nouveau_audio_mode_set(encoder, mode); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_hwsq.h b/drivers/gpu/drm/nouveau/nouveau_hwsq.h new file mode 100644 index 000000000000..697687593a81 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_hwsq.h @@ -0,0 +1,115 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#ifndef __NOUVEAU_HWSQ_H__ +#define __NOUVEAU_HWSQ_H__ + +struct hwsq_ucode { + u8 data[0x200]; + union { + u8 *u08; + u16 *u16; + u32 *u32; + } ptr; + u16 len; + + u32 reg; + u32 val; +}; + +static inline void +hwsq_init(struct hwsq_ucode *hwsq) +{ + hwsq->ptr.u08 = hwsq->data; + hwsq->reg = 0xffffffff; + hwsq->val = 0xffffffff; +} + +static inline void +hwsq_fini(struct hwsq_ucode *hwsq) +{ + do { + *hwsq->ptr.u08++ = 0x7f; + hwsq->len = hwsq->ptr.u08 - hwsq->data; + } while (hwsq->len & 3); + hwsq->ptr.u08 = hwsq->data; +} + +static inline void +hwsq_usec(struct hwsq_ucode *hwsq, u8 usec) +{ + u32 shift = 0; + while (usec & ~3) { + usec >>= 2; + shift++; + } + + *hwsq->ptr.u08++ = (shift << 2) | usec; +} + +static inline void +hwsq_setf(struct hwsq_ucode *hwsq, u8 flag, int val) +{ + flag += 0x80; + if (val >= 0) + flag += 0x20; + if (val >= 1) + flag += 0x20; + *hwsq->ptr.u08++ = flag; +} + +static inline void +hwsq_op5f(struct hwsq_ucode *hwsq, u8 v0, u8 v1) +{ + *hwsq->ptr.u08++ = 0x5f; + *hwsq->ptr.u08++ = v0; + *hwsq->ptr.u08++ = v1; +} + +static inline void +hwsq_wr32(struct hwsq_ucode *hwsq, u32 reg, u32 val) +{ + if (val != hwsq->val) { + if ((val & 0xffff0000) == (hwsq->val & 0xffff0000)) { + *hwsq->ptr.u08++ = 0x42; + *hwsq->ptr.u16++ = (val & 0x0000ffff); + } else { + *hwsq->ptr.u08++ = 0xe2; + *hwsq->ptr.u32++ = val; + } + + hwsq->val = val; + } + + if ((reg & 0xffff0000) == (hwsq->reg & 0xffff0000)) { + *hwsq->ptr.u08++ = 0x40; + *hwsq->ptr.u16++ = (reg & 0x0000ffff); + } else { + *hwsq->ptr.u08++ = 0xe0; + *hwsq->ptr.u32++ = reg; + } + hwsq->reg = reg; +} + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index d39b2202b197..820ae7f52044 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -29,262 +29,465 @@ #include "nouveau_i2c.h" #include "nouveau_hw.h" +#define T_TIMEOUT 2200000 +#define T_RISEFALL 1000 +#define T_HOLD 5000 + static void -nv04_i2c_setscl(void *data, int state) +i2c_drive_scl(void *data, int state) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0); - NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); + struct nouveau_i2c_chan *port = data; + if (port->type == 0) { + u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); + if (state) val |= 0x20; + else val &= 0xdf; + NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); + } else + if (port->type == 4) { + nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01); + } else + if (port->type == 5) { + if (state) port->state |= 0x01; + else port->state &= 0xfe; + nv_wr32(port->dev, port->drive, 4 | port->state); + } } static void -nv04_i2c_setsda(void *data, int state) +i2c_drive_sda(void *data, int state) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0); - NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); + struct nouveau_i2c_chan *port = data; + if (port->type == 0) { + u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); + if (state) val |= 0x10; + else val &= 0xef; + NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); + } else + if (port->type == 4) { + nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01); + } else + if (port->type == 5) { + if (state) port->state |= 0x02; + else port->state &= 0xfd; + nv_wr32(port->dev, port->drive, 4 | port->state); + } } static int -nv04_i2c_getscl(void *data) +i2c_sense_scl(void *data) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4); + struct nouveau_i2c_chan *port = data; + struct drm_nouveau_private *dev_priv = port->dev->dev_private; + if (port->type == 0) { + return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x04); + } else + if (port->type == 4) { + return !!(nv_rd32(port->dev, port->sense) & 0x00040000); + } else + if (port->type == 5) { + if (dev_priv->card_type < NV_D0) + return !!(nv_rd32(port->dev, port->sense) & 0x01); + else + return !!(nv_rd32(port->dev, port->sense) & 0x10); + } + return 0; } static int -nv04_i2c_getsda(void *data) +i2c_sense_sda(void *data) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8); + struct nouveau_i2c_chan *port = data; + struct drm_nouveau_private *dev_priv = port->dev->dev_private; + if (port->type == 0) { + return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x08); + } else + if (port->type == 4) { + return !!(nv_rd32(port->dev, port->sense) & 0x00080000); + } else + if (port->type == 5) { + if (dev_priv->card_type < NV_D0) + return !!(nv_rd32(port->dev, port->sense) & 0x02); + else + return !!(nv_rd32(port->dev, port->sense) & 0x20); + } + return 0; } static void -nv4e_i2c_setscl(void *data, int state) +i2c_delay(struct nouveau_i2c_chan *port, u32 nsec) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0); - nv_wr32(dev, i2c->wr, val | 0x01); + udelay((nsec + 500) / 1000); } -static void -nv4e_i2c_setsda(void *data, int state) +static bool +i2c_raise_scl(struct nouveau_i2c_chan *port) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; + u32 timeout = T_TIMEOUT / T_RISEFALL; + + i2c_drive_scl(port, 1); + do { + i2c_delay(port, T_RISEFALL); + } while (!i2c_sense_scl(port) && --timeout); - val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0); - nv_wr32(dev, i2c->wr, val | 0x01); + return timeout != 0; } static int -nv4e_i2c_getscl(void *data) +i2c_start(struct nouveau_i2c_chan *port) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; + int ret = 0; + + port->state = i2c_sense_scl(port); + port->state |= i2c_sense_sda(port) << 1; + if (port->state != 3) { + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + if (!i2c_raise_scl(port)) + ret = -EBUSY; + } - return !!((nv_rd32(dev, i2c->rd) >> 16) & 4); + i2c_drive_sda(port, 0); + i2c_delay(port, T_HOLD); + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return ret; } -static int -nv4e_i2c_getsda(void *data) +static void +i2c_stop(struct nouveau_i2c_chan *port) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 0); + i2c_delay(port, T_RISEFALL); + + i2c_drive_scl(port, 1); + i2c_delay(port, T_HOLD); + i2c_drive_sda(port, 1); + i2c_delay(port, T_HOLD); } -static const uint32_t nv50_i2c_port[] = { - 0x00e138, 0x00e150, 0x00e168, 0x00e180, - 0x00e254, 0x00e274, 0x00e764, 0x00e780, - 0x00e79c, 0x00e7b8 -}; -#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) - static int -nv50_i2c_getscl(void *data) +i2c_bitw(struct nouveau_i2c_chan *port, int sda) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; + i2c_drive_sda(port, sda); + i2c_delay(port, T_RISEFALL); - return !!(nv_rd32(dev, i2c->rd) & 1); -} + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return 0; +} static int -nv50_i2c_getsda(void *data) +i2c_bitr(struct nouveau_i2c_chan *port) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; + int sda; + + i2c_drive_sda(port, 1); + i2c_delay(port, T_RISEFALL); - return !!(nv_rd32(dev, i2c->rd) & 2); + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + + sda = i2c_sense_sda(port); + + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return sda; } -static void -nv50_i2c_setscl(void *data, int state) +static int +i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last) { - struct nouveau_i2c_chan *i2c = data; + int i, bit; + + *byte = 0; + for (i = 7; i >= 0; i--) { + bit = i2c_bitr(port); + if (bit < 0) + return bit; + *byte |= bit << i; + } - nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); + return i2c_bitw(port, last ? 1 : 0); } -static void -nv50_i2c_setsda(void *data, int state) +static int +i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte) { - struct nouveau_i2c_chan *i2c = data; + int i, ret; + for (i = 7; i >= 0; i--) { + ret = i2c_bitw(port, !!(byte & (1 << i))); + if (ret < 0) + return ret; + } - nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0)); - i2c->data = state; + ret = i2c_bitr(port); + if (ret == 1) /* nack */ + ret = -EIO; + return ret; } static int -nvd0_i2c_getscl(void *data) +i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg) { - struct nouveau_i2c_chan *i2c = data; - return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10); + u32 addr = msg->addr << 1; + if (msg->flags & I2C_M_RD) + addr |= 1; + return i2c_put_byte(port, addr); } static int -nvd0_i2c_getsda(void *data) +i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - struct nouveau_i2c_chan *i2c = data; - return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20); + struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap; + struct i2c_msg *msg = msgs; + int ret = 0, mcnt = num; + + while (!ret && mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; + + ret = i2c_start(port); + if (ret == 0) + ret = i2c_addr(port, msg); + + if (msg->flags & I2C_M_RD) { + while (!ret && remaining--) + ret = i2c_get_byte(port, ptr++, !remaining); + } else { + while (!ret && remaining--) + ret = i2c_put_byte(port, *ptr++); + } + + msg++; + } + + i2c_stop(port); + return (ret < 0) ? ret : num; } -int -nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) +static u32 +i2c_bit_func(struct i2c_adapter *adap) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_i2c_chan *i2c; - int ret; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +const struct i2c_algorithm i2c_bit_algo = { + .master_xfer = i2c_bit_xfer, + .functionality = i2c_bit_func +}; + +static const uint32_t nv50_i2c_port[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 +}; - if (entry->chan) - return -EEXIST; +static u8 * +i2c_table(struct drm_device *dev, u8 *version) +{ + u8 *dcb = dcb_table(dev), *i2c = NULL; + if (dcb) { + if (dcb[0] >= 0x15) + i2c = ROMPTR(dev, dcb[2]); + if (dcb[0] >= 0x30) + i2c = ROMPTR(dev, dcb[4]); + } - if (dev_priv->card_type >= NV_50 && - dev_priv->card_type <= NV_C0 && entry->read >= NV50_I2C_PORTS) { - NV_ERROR(dev, "unknown i2c port %d\n", entry->read); - return -EINVAL; + /* early revisions had no version number, use dcb version */ + if (i2c) { + *version = dcb[0]; + if (*version >= 0x30) + *version = i2c[0]; } - i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); - if (i2c == NULL) - return -ENOMEM; - - switch (entry->port_type) { - case 0: - i2c->bit.setsda = nv04_i2c_setsda; - i2c->bit.setscl = nv04_i2c_setscl; - i2c->bit.getsda = nv04_i2c_getsda; - i2c->bit.getscl = nv04_i2c_getscl; - i2c->rd = entry->read; - i2c->wr = entry->write; - break; - case 4: - i2c->bit.setsda = nv4e_i2c_setsda; - i2c->bit.setscl = nv4e_i2c_setscl; - i2c->bit.getsda = nv4e_i2c_getsda; - i2c->bit.getscl = nv4e_i2c_getscl; - i2c->rd = 0x600800 + entry->read; - i2c->wr = 0x600800 + entry->write; - break; - case 5: - i2c->bit.setsda = nv50_i2c_setsda; - i2c->bit.setscl = nv50_i2c_setscl; - if (dev_priv->card_type < NV_D0) { - i2c->bit.getsda = nv50_i2c_getsda; - i2c->bit.getscl = nv50_i2c_getscl; - i2c->rd = nv50_i2c_port[entry->read]; - i2c->wr = i2c->rd; - } else { - i2c->bit.getsda = nvd0_i2c_getsda; - i2c->bit.getscl = nvd0_i2c_getscl; - i2c->rd = 0x00d014 + (entry->read * 0x20); - i2c->wr = i2c->rd; - } - break; - case 6: - i2c->rd = entry->read; - i2c->wr = entry->write; - break; - default: - NV_ERROR(dev, "DCB I2C port type %d unknown\n", - entry->port_type); - kfree(i2c); - return -EINVAL; + return i2c; +} + +int +nouveau_i2c_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct nouveau_i2c_chan *port; + u8 *i2c, *entry, legacy[2][4] = {}; + u8 version, entries, recordlen; + int ret, i; + + INIT_LIST_HEAD(&dev_priv->i2c_ports); + + i2c = i2c_table(dev, &version); + if (!i2c) { + u8 *bmp = &bios->data[bios->offset]; + if (bios->type != NVBIOS_BMP) + return -ENODEV; + + legacy[0][0] = NV_CIO_CRE_DDC_WR__INDEX; + legacy[0][1] = NV_CIO_CRE_DDC_STATUS__INDEX; + legacy[1][0] = NV_CIO_CRE_DDC0_WR__INDEX; + legacy[1][1] = NV_CIO_CRE_DDC0_STATUS__INDEX; + + /* BMP (from v4.0) has i2c info in the structure, it's in a + * fixed location on earlier VBIOS + */ + if (bmp[5] < 4) + i2c = &bios->data[0x48]; + else + i2c = &bmp[0x36]; + + if (i2c[4]) legacy[0][0] = i2c[4]; + if (i2c[5]) legacy[0][1] = i2c[5]; + if (i2c[6]) legacy[1][0] = i2c[6]; + if (i2c[7]) legacy[1][1] = i2c[7]; } - snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), - "nouveau-%s-%d", pci_name(dev->pdev), index); - i2c->adapter.owner = THIS_MODULE; - i2c->adapter.dev.parent = &dev->pdev->dev; - i2c->dev = dev; - i2c_set_adapdata(&i2c->adapter, i2c); - - if (entry->port_type < 6) { - i2c->adapter.algo_data = &i2c->bit; - i2c->bit.udelay = 40; - i2c->bit.timeout = usecs_to_jiffies(5000); - i2c->bit.data = i2c; - ret = i2c_bit_add_bus(&i2c->adapter); + if (i2c && version >= 0x30) { + entry = i2c[1] + i2c; + entries = i2c[2]; + recordlen = i2c[3]; + } else + if (i2c) { + entry = i2c; + entries = 16; + recordlen = 4; } else { - i2c->adapter.algo = &nouveau_dp_i2c_algo; - ret = i2c_add_adapter(&i2c->adapter); + entry = legacy[0]; + entries = 2; + recordlen = 4; } - if (ret) { - NV_ERROR(dev, "Failed to register i2c %d\n", index); - kfree(i2c); - return ret; + for (i = 0; i < entries; i++, entry += recordlen) { + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (port == NULL) { + nouveau_i2c_fini(dev); + return -ENOMEM; + } + + port->type = entry[3]; + if (version < 0x30) { + port->type &= 0x07; + if (port->type == 0x07) + port->type = 0xff; + } + + if (port->type == 0xff) { + kfree(port); + continue; + } + + switch (port->type) { + case 0: /* NV04:NV50 */ + port->drive = entry[0]; + port->sense = entry[1]; + port->adapter.algo = &i2c_bit_algo; + break; + case 4: /* NV4E */ + port->drive = 0x600800 + entry[1]; + port->sense = port->drive; + port->adapter.algo = &i2c_bit_algo; + break; + case 5: /* NV50- */ + port->drive = entry[0] & 0x0f; + if (dev_priv->card_type < NV_D0) { + if (port->drive >= ARRAY_SIZE(nv50_i2c_port)) + break; + port->drive = nv50_i2c_port[port->drive]; + port->sense = port->drive; + } else { + port->drive = 0x00d014 + (port->drive * 0x20); + port->sense = port->drive; + } + port->adapter.algo = &i2c_bit_algo; + break; + case 6: /* NV50- DP AUX */ + port->drive = entry[0]; + port->sense = port->drive; + port->adapter.algo = &nouveau_dp_i2c_algo; + break; + default: + break; + } + + if (!port->adapter.algo) { + NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", + i, port->type, port->drive, port->sense); + kfree(port); + continue; + } + + snprintf(port->adapter.name, sizeof(port->adapter.name), + "nouveau-%s-%d", pci_name(dev->pdev), i); + port->adapter.owner = THIS_MODULE; + port->adapter.dev.parent = &dev->pdev->dev; + port->dev = dev; + port->index = i; + port->dcb = ROM32(entry[0]); + i2c_set_adapdata(&port->adapter, i2c); + + ret = i2c_add_adapter(&port->adapter); + if (ret) { + NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); + kfree(port); + continue; + } + + list_add_tail(&port->head, &dev_priv->i2c_ports); } - entry->chan = i2c; return 0; } void -nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry) +nouveau_i2c_fini(struct drm_device *dev) { - if (!entry->chan) - return; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_i2c_chan *port, *tmp; - i2c_del_adapter(&entry->chan->adapter); - kfree(entry->chan); - entry->chan = NULL; + list_for_each_entry_safe(port, tmp, &dev_priv->i2c_ports, head) { + i2c_del_adapter(&port->adapter); + kfree(port); + } } struct nouveau_i2c_chan * -nouveau_i2c_find(struct drm_device *dev, int index) +nouveau_i2c_find(struct drm_device *dev, u8 index) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_i2c_entry *i2c = &dev_priv->vbios.dcb.i2c[index]; + struct nouveau_i2c_chan *port; + + if (index == NV_I2C_DEFAULT(0) || + index == NV_I2C_DEFAULT(1)) { + u8 version, *i2c = i2c_table(dev, &version); + if (i2c && version >= 0x30) { + if (index == NV_I2C_DEFAULT(0)) + index = (i2c[4] & 0x0f); + else + index = (i2c[4] & 0xf0) >> 4; + } else { + index = 2; + } + } - if (index >= DCB_MAX_NUM_I2C_ENTRIES) - return NULL; + list_for_each_entry(port, &dev_priv->i2c_ports, head) { + if (port->index == index) + break; + } - if (dev_priv->card_type >= NV_50 && (i2c->entry & 0x00000100)) { - uint32_t reg = 0xe500, val; + if (&port->head == &dev_priv->i2c_ports) + return NULL; - if (i2c->port_type == 6) { - reg += i2c->read * 0x50; + if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) { + u32 reg = 0x00e500, val; + if (port->type == 6) { + reg += port->drive * 0x50; val = 0x2002; } else { - reg += ((i2c->entry & 0x1e00) >> 9) * 0x50; + reg += ((port->dcb & 0x1e00) >> 9) * 0x50; val = 0xe001; } @@ -294,9 +497,7 @@ nouveau_i2c_find(struct drm_device *dev, int index) nv_mask(dev, reg + 0x00, 0x0000f003, val); } - if (!i2c->chan && nouveau_i2c_init(dev, i2c, index)) - return NULL; - return i2c->chan; + return port; } bool @@ -331,9 +532,13 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what, struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index); int i; - NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index); + if (!i2c) { + NV_DEBUG(dev, "No bus when probing %s on %d\n", what, index); + return -ENODEV; + } - for (i = 0; i2c && info[i].addr; i++) { + NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, i2c->index); + for (i = 0; info[i].addr; i++) { if (nouveau_probe_i2c_addr(i2c, info[i].addr) && (!match || match(i2c, &info[i]))) { NV_INFO(dev, "Detected %s: %s\n", what, info[i].type); @@ -342,6 +547,5 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what, } NV_DEBUG(dev, "No devices found.\n"); - return -ENODEV; } diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index 422b62fd8272..4d2e4e9031be 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -27,20 +27,25 @@ #include <linux/i2c-algo-bit.h> #include "drm_dp_helper.h" -struct dcb_i2c_entry; +#define NV_I2C_PORT(n) (0x00 + (n)) +#define NV_I2C_PORT_NUM 0x10 +#define NV_I2C_DEFAULT(n) (0x80 + (n)) struct nouveau_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; - struct i2c_algo_bit_data bit; - unsigned rd; - unsigned wr; - unsigned data; + struct list_head head; + u8 index; + u8 type; + u32 dcb; + u32 drive; + u32 sense; + u32 state; }; -int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index); -void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *); -struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index); +int nouveau_i2c_init(struct drm_device *); +void nouveau_i2c_fini(struct drm_device *); +struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, u8 index); bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr); int nouveau_i2c_identify(struct drm_device *dev, const char *what, struct i2c_board_info *info, diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 37fcaa260e98..c3a5745e9c79 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -644,10 +644,10 @@ nouveau_mem_timing_init(struct drm_device *dev) return; if (P.version == 1) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[4]); else if (P.version == 2) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[8]); else { NV_WARN(dev, "unknown mem for BIT P %d\n", P.version); } diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c new file mode 100644 index 000000000000..8bccddf4eff0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -0,0 +1,677 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#include <linux/acpi.h> + +#include "drmP.h" +#include "nouveau_drv.h" + +#define MXM_DBG(dev, fmt, args...) NV_DEBUG((dev), "MXM: " fmt, ##args) +#define MXM_MSG(dev, fmt, args...) NV_INFO((dev), "MXM: " fmt, ##args) + +static u8 * +mxms_data(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + return dev_priv->mxms; + +} + +static u16 +mxms_version(struct drm_device *dev) +{ + u8 *mxms = mxms_data(dev); + u16 version = (mxms[4] << 8) | mxms[5]; + switch (version ) { + case 0x0200: + case 0x0201: + case 0x0300: + return version; + default: + break; + } + + MXM_DBG(dev, "unknown version %d.%d\n", mxms[4], mxms[5]); + return 0x0000; +} + +static u16 +mxms_headerlen(struct drm_device *dev) +{ + return 8; +} + +static u16 +mxms_structlen(struct drm_device *dev) +{ + return *(u16 *)&mxms_data(dev)[6]; +} + +static bool +mxms_checksum(struct drm_device *dev) +{ + u16 size = mxms_headerlen(dev) + mxms_structlen(dev); + u8 *mxms = mxms_data(dev), sum = 0; + while (size--) + sum += *mxms++; + if (sum) { + MXM_DBG(dev, "checksum invalid\n"); + return false; + } + return true; +} + +static bool +mxms_valid(struct drm_device *dev) +{ + u8 *mxms = mxms_data(dev); + if (*(u32 *)mxms != 0x5f4d584d) { + MXM_DBG(dev, "signature invalid\n"); + return false; + } + + if (!mxms_version(dev) || !mxms_checksum(dev)) + return false; + + return true; +} + +static bool +mxms_foreach(struct drm_device *dev, u8 types, + bool (*exec)(struct drm_device *, u8 *, void *), void *info) +{ + u8 *mxms = mxms_data(dev); + u8 *desc = mxms + mxms_headerlen(dev); + u8 *fini = desc + mxms_structlen(dev) - 1; + while (desc < fini) { + u8 type = desc[0] & 0x0f; + u8 headerlen = 0; + u8 recordlen = 0; + u8 entries = 0; + + switch (type) { + case 0: /* Output Device Structure */ + if (mxms_version(dev) >= 0x0300) + headerlen = 8; + else + headerlen = 6; + break; + case 1: /* System Cooling Capability Structure */ + case 2: /* Thermal Structure */ + case 3: /* Input Power Structure */ + headerlen = 4; + break; + case 4: /* GPIO Device Structure */ + headerlen = 4; + recordlen = 2; + entries = (ROM32(desc[0]) & 0x01f00000) >> 20; + break; + case 5: /* Vendor Specific Structure */ + headerlen = 8; + break; + case 6: /* Backlight Control Structure */ + if (mxms_version(dev) >= 0x0300) { + headerlen = 4; + recordlen = 8; + entries = (desc[1] & 0xf0) >> 4; + } else { + headerlen = 8; + } + break; + case 7: /* Fan Control Structure */ + headerlen = 8; + recordlen = 4; + entries = desc[1] & 0x07; + break; + default: + MXM_DBG(dev, "unknown descriptor type %d\n", type); + return false; + } + + if ((drm_debug & DRM_UT_DRIVER) && (exec == NULL)) { + static const char * mxms_desc_name[] = { + "ODS", "SCCS", "TS", "IPS", + "GSD", "VSS", "BCS", "FCS", + }; + u8 *dump = desc; + int i, j; + + MXM_DBG(dev, "%4s: ", mxms_desc_name[type]); + for (j = headerlen - 1; j >= 0; j--) + printk("%02x", dump[j]); + printk("\n"); + dump += headerlen; + + for (i = 0; i < entries; i++, dump += recordlen) { + MXM_DBG(dev, " "); + for (j = recordlen - 1; j >= 0; j--) + printk("%02x", dump[j]); + printk("\n"); + } + } + + if (types & (1 << type)) { + if (!exec(dev, desc, info)) + return false; + } + + desc += headerlen + (entries * recordlen); + } + + return true; +} + +static u8 * +mxm_table(struct drm_device *dev, u8 *size) +{ + struct bit_entry x; + + if (bit_table(dev, 'x', &x)) { + MXM_DBG(dev, "BIT 'x' table not present\n"); + return NULL; + } + + if (x.version != 1 || x.length < 3) { + MXM_MSG(dev, "BIT x table %d/%d unknown\n", + x.version, x.length); + return NULL; + } + + *size = x.length; + return x.data; +} + +/* These map MXM v2.x digital connection values to the appropriate SOR/link, + * hopefully they're correct for all boards within the same chipset... + * + * MXM v3.x VBIOS are nicer and provide pointers to these tables. + */ +static u8 nv84_sor_map[16] = { + 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv92_sor_map[16] = { + 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv94_sor_map[16] = { + 0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv96_sor_map[16] = { + 0x00, 0x14, 0x24, 0x00, 0x34, 0x00, 0x11, 0x31, + 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv98_sor_map[16] = { + 0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 +mxm_sor_map(struct drm_device *dev, u8 conn) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u8 len, *mxm = mxm_table(dev, &len); + if (mxm && len >= 6) { + u8 *map = ROMPTR(dev, mxm[4]); + if (map) { + if (map[0] == 0x10) { + if (conn < map[3]) + return map[map[1] + conn]; + return 0x00; + } + + MXM_MSG(dev, "unknown sor map 0x%02x\n", map[0]); + } + } + + if (dev_priv->chipset == 0x84 || dev_priv->chipset == 0x86) + return nv84_sor_map[conn]; + if (dev_priv->chipset == 0x92) + return nv92_sor_map[conn]; + if (dev_priv->chipset == 0x94) + return nv94_sor_map[conn]; + if (dev_priv->chipset == 0x96) + return nv96_sor_map[conn]; + if (dev_priv->chipset == 0x98) + return nv98_sor_map[conn]; + + MXM_MSG(dev, "missing sor map\n"); + return 0x00; +} + +static u8 +mxm_ddc_map(struct drm_device *dev, u8 port) +{ + u8 len, *mxm = mxm_table(dev, &len); + if (mxm && len >= 8) { + u8 *map = ROMPTR(dev, mxm[6]); + if (map) { + if (map[0] == 0x10) { + if (port < map[3]) + return map[map[1] + port]; + return 0x00; + } + + MXM_MSG(dev, "unknown ddc map 0x%02x\n", map[0]); + } + } + + /* v2.x: directly write port as dcb i2cidx */ + return (port << 4) | port; +} + +struct mxms_odev { + u8 outp_type; + u8 conn_type; + u8 ddc_port; + u8 dig_conn; +}; + +static void +mxms_output_device(struct drm_device *dev, u8 *pdata, struct mxms_odev *desc) +{ + u64 data = ROM32(pdata[0]); + if (mxms_version(dev) >= 0x0300) + data |= (u64)ROM16(pdata[4]) << 32; + + desc->outp_type = (data & 0x00000000000000f0ULL) >> 4; + desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8; + desc->conn_type = (data & 0x000000000001f000ULL) >> 12; + desc->dig_conn = (data & 0x0000000000780000ULL) >> 19; +} + +struct context { + u32 *outp; + struct mxms_odev desc; +}; + +static bool +mxm_match_tmds_partner(struct drm_device *dev, u8 *data, void *info) +{ + struct context *ctx = info; + struct mxms_odev desc; + + mxms_output_device(dev, data, &desc); + if (desc.outp_type == 2 && + desc.dig_conn == ctx->desc.dig_conn) + return false; + return true; +} + +static bool +mxm_match_dcb(struct drm_device *dev, u8 *data, void *info) +{ + struct context *ctx = info; + u64 desc = *(u64 *)data; + + mxms_output_device(dev, data, &ctx->desc); + + /* match dcb encoder type to mxm-ods device type */ + if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type) + return true; + + /* digital output, have some extra stuff to match here, there's a + * table in the vbios that provides a mapping from the mxm digital + * connection enum values to SOR/link + */ + if ((desc & 0x00000000000000f0) >= 0x20) { + /* check against sor index */ + u8 link = mxm_sor_map(dev, ctx->desc.dig_conn); + if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24) + return true; + + /* check dcb entry has a compatible link field */ + link = (link & 0x30) >> 4; + if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link) + return true; + } + + /* mark this descriptor accounted for by setting invalid device type, + * except of course some manufactures don't follow specs properly and + * we need to avoid killing off the TMDS function on DP connectors + * if MXM-SIS is missing an entry for it. + */ + data[0] &= ~0xf0; + if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 && + mxms_foreach(dev, 0x01, mxm_match_tmds_partner, ctx)) { + data[0] |= 0x20; /* modify descriptor to match TMDS now */ + } else { + data[0] |= 0xf0; + } + + return false; +} + +static int +mxm_dcb_sanitise_entry(struct drm_device *dev, void *data, int idx, u8 *dcbe) +{ + struct context ctx = { .outp = (u32 *)dcbe }; + u8 type, i2cidx, link; + u8 *conn; + + /* look for an output device structure that matches this dcb entry. + * if one isn't found, disable it. + */ + if (mxms_foreach(dev, 0x01, mxm_match_dcb, &ctx)) { + MXM_DBG(dev, "disable %d: 0x%08x 0x%08x\n", + idx, ctx.outp[0], ctx.outp[1]); + ctx.outp[0] |= 0x0000000f; + return 0; + } + + /* modify the output's ddc/aux port, there's a pointer to a table + * with the mapping from mxm ddc/aux port to dcb i2c_index in the + * vbios mxm table + */ + i2cidx = mxm_ddc_map(dev, ctx.desc.ddc_port); + if ((ctx.outp[0] & 0x0000000f) != OUTPUT_DP) + i2cidx = (i2cidx & 0x0f) << 4; + else + i2cidx = (i2cidx & 0xf0); + + if (i2cidx != 0xf0) { + ctx.outp[0] &= ~0x000000f0; + ctx.outp[0] |= i2cidx; + } + + /* override dcb sorconf.link, based on what mxm data says */ + switch (ctx.desc.outp_type) { + case 0x00: /* Analog CRT */ + case 0x01: /* Analog TV/HDTV */ + break; + default: + link = mxm_sor_map(dev, ctx.desc.dig_conn) & 0x30; + ctx.outp[1] &= ~0x00000030; + ctx.outp[1] |= link; + break; + } + + /* we may need to fixup various other vbios tables based on what + * the descriptor says the connector type should be. + * + * in a lot of cases, the vbios tables will claim DVI-I is possible, + * and the mxm data says the connector is really HDMI. another + * common example is DP->eDP. + */ + conn = dcb_conn(dev, (ctx.outp[0] & 0x0000f000) >> 12); + type = conn[0]; + switch (ctx.desc.conn_type) { + case 0x01: /* LVDS */ + ctx.outp[1] |= 0x00000004; /* use_power_scripts */ + /* XXX: modify default link width in LVDS table */ + break; + case 0x02: /* HDMI */ + type = DCB_CONNECTOR_HDMI_1; + break; + case 0x03: /* DVI-D */ + type = DCB_CONNECTOR_DVI_D; + break; + case 0x0e: /* eDP, falls through to DPint */ + ctx.outp[1] |= 0x00010000; + case 0x07: /* DP internal, wtf is this?? HP8670w */ + ctx.outp[1] |= 0x00000004; /* use_power_scripts? */ + type = DCB_CONNECTOR_eDP; + break; + default: + break; + } + + if (mxms_version(dev) >= 0x0300) + conn[0] = type; + + return 0; +} + +static bool +mxm_show_unmatched(struct drm_device *dev, u8 *data, void *info) +{ + u64 desc = *(u64 *)data; + if ((desc & 0xf0) != 0xf0) + MXM_MSG(dev, "unmatched output device 0x%016llx\n", desc); + return true; +} + +static void +mxm_dcb_sanitise(struct drm_device *dev) +{ + u8 *dcb = dcb_table(dev); + if (!dcb || dcb[0] != 0x40) { + MXM_DBG(dev, "unsupported DCB version\n"); + return; + } + + dcb_outp_foreach(dev, NULL, mxm_dcb_sanitise_entry); + mxms_foreach(dev, 0x01, mxm_show_unmatched, NULL); +} + +static bool +mxm_shadow_rom_fetch(struct nouveau_i2c_chan *i2c, u8 addr, + u8 offset, u8 size, u8 *data) +{ + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 1, .buf = &offset }, + { .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, }, + }; + + return i2c_transfer(&i2c->adapter, msgs, 2) == 2; +} + +static bool +mxm_shadow_rom(struct drm_device *dev, u8 version) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_i2c_chan *i2c = NULL; + u8 i2cidx, mxms[6], addr, size; + + i2cidx = mxm_ddc_map(dev, 1 /* LVDS_DDC */) & 0x0f; + if (i2cidx < 0x0f) + i2c = nouveau_i2c_find(dev, i2cidx); + if (!i2c) + return false; + + addr = 0x54; + if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms)) { + addr = 0x56; + if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms)) + return false; + } + + dev_priv->mxms = mxms; + size = mxms_headerlen(dev) + mxms_structlen(dev); + dev_priv->mxms = kmalloc(size, GFP_KERNEL); + + if (dev_priv->mxms && + mxm_shadow_rom_fetch(i2c, addr, 0, size, dev_priv->mxms)) + return true; + + kfree(dev_priv->mxms); + dev_priv->mxms = NULL; + return false; +} + +#if defined(CONFIG_ACPI) +static bool +mxm_shadow_dsm(struct drm_device *dev, u8 version) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + static char muid[] = { + 0x00, 0xA4, 0x04, 0x40, 0x7D, 0x91, 0xF2, 0x4C, + 0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65 + }; + u32 mxms_args[] = { 0x00000000 }; + union acpi_object args[4] = { + /* _DSM MUID */ + { .buffer.type = 3, + .buffer.length = sizeof(muid), + .buffer.pointer = muid, + }, + /* spec says this can be zero to mean "highest revision", but + * of course there's at least one bios out there which fails + * unless you pass in exactly the version it supports.. + */ + { .integer.type = ACPI_TYPE_INTEGER, + .integer.value = (version & 0xf0) << 4 | (version & 0x0f), + }, + /* MXMS function */ + { .integer.type = ACPI_TYPE_INTEGER, + .integer.value = 0x00000010, + }, + /* Pointer to MXMS arguments */ + { .buffer.type = ACPI_TYPE_BUFFER, + .buffer.length = sizeof(mxms_args), + .buffer.pointer = (char *)mxms_args, + }, + }; + struct acpi_object_list list = { ARRAY_SIZE(args), args }; + struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_handle handle; + int ret; + + handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); + if (!handle) + return false; + + ret = acpi_evaluate_object(handle, "_DSM", &list, &retn); + if (ret) { + MXM_DBG(dev, "DSM MXMS failed: %d\n", ret); + return false; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_BUFFER) { + dev_priv->mxms = kmemdup(obj->buffer.pointer, + obj->buffer.length, GFP_KERNEL); + } else + if (obj->type == ACPI_TYPE_INTEGER) { + MXM_DBG(dev, "DSM MXMS returned 0x%llx\n", obj->integer.value); + } + + kfree(obj); + return dev_priv->mxms != NULL; +} +#endif + +#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) + +#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" + +static bool +mxm_shadow_wmi(struct drm_device *dev, u8 version) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 }; + struct acpi_buffer args = { sizeof(mxms_args), mxms_args }; + struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + if (!wmi_has_guid(WMI_WMMX_GUID)) + return false; + + status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); + if (ACPI_FAILURE(status)) { + MXM_DBG(dev, "WMMX MXMS returned %d\n", status); + return false; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_BUFFER) { + dev_priv->mxms = kmemdup(obj->buffer.pointer, + obj->buffer.length, GFP_KERNEL); + } + + kfree(obj); + return dev_priv->mxms != NULL; +} +#endif + +struct mxm_shadow_h { + const char *name; + bool (*exec)(struct drm_device *, u8 version); +} _mxm_shadow[] = { + { "ROM", mxm_shadow_rom }, +#if defined(CONFIG_ACPI) + { "DSM", mxm_shadow_dsm }, +#endif +#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) + { "WMI", mxm_shadow_wmi }, +#endif + {} +}; + +static int +mxm_shadow(struct drm_device *dev, u8 version) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct mxm_shadow_h *shadow = _mxm_shadow; + do { + MXM_DBG(dev, "checking %s\n", shadow->name); + if (shadow->exec(dev, version)) { + if (mxms_valid(dev)) + return 0; + kfree(dev_priv->mxms); + dev_priv->mxms = NULL; + } + } while ((++shadow)->name); + return -ENOENT; +} + +int +nouveau_mxm_init(struct drm_device *dev) +{ + u8 mxm_size, *mxm = mxm_table(dev, &mxm_size); + if (!mxm || !mxm[0]) { + MXM_MSG(dev, "no VBIOS data, nothing to do\n"); + return 0; + } + + MXM_MSG(dev, "BIOS version %d.%d\n", mxm[0] >> 4, mxm[0] & 0x0f); + + if (mxm_shadow(dev, mxm[0])) { + MXM_MSG(dev, "failed to locate valid SIS\n"); + return -EINVAL; + } + + MXM_MSG(dev, "MXMS Version %d.%d\n", + mxms_version(dev) >> 8, mxms_version(dev) & 0xff); + mxms_foreach(dev, 0, NULL, NULL); + + if (nouveau_mxmdcb) + mxm_dcb_sanitise(dev); + return 0; +} + +void +nouveau_mxm_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + kfree(dev_priv->mxms); + dev_priv->mxms = NULL; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c index 6abdbe6530a7..2ef883c4bbc1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_notifier.c +++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c @@ -115,7 +115,7 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *nobj = NULL; struct drm_mm_node *mem; - uint32_t offset; + uint64_t offset; int target, ret; mem = drm_mm_search_free_in_range(&chan->notifier_heap, size, 0, diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 960c0ae0c0c3..cc419fae794b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -723,14 +723,14 @@ nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm) nv_wo32(chan->ramin, 0x020c, 0x000000ff); /* map display semaphore buffers into channel's vm */ - if (dev_priv->card_type >= NV_D0) - return 0; - - for (i = 0; i < 2; i++) { - struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i]; - - ret = nouveau_bo_vma_add(dispc->sem.bo, chan->vm, - &chan->dispc_vma[i]); + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo; + if (dev_priv->card_type >= NV_D0) + bo = nvd0_display_crtc_sema(dev, i); + else + bo = nv50_display(dev)->crtc[i].sem.bo; + + ret = nouveau_bo_vma_add(bo, chan->vm, &chan->dispc_vma[i]); if (ret) return ret; } @@ -879,9 +879,14 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) NV_DEBUG(dev, "ch%d\n", chan->id); - if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) { + if (dev_priv->card_type >= NV_D0) { + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i); + nouveau_bo_vma_del(bo, &chan->dispc_vma[i]); + } + } else + if (dev_priv->card_type >= NV_50) { struct nv50_display *disp = nv50_display(dev); - for (i = 0; i < dev->mode_config.num_crtc; i++) { struct nv50_display_crtc *dispc = &disp->crtc[i]; nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]); diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 33d03fbf00df..58f497343cec 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -41,7 +41,7 @@ legacy_perf_init(struct drm_device *dev) return; } - perf = ROMPTR(bios, bmp[0x73]); + perf = ROMPTR(dev, bmp[0x73]); if (!perf) { NV_DEBUG(dev, "No memclock table pointer found.\n"); return; @@ -87,7 +87,7 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, * ramcfg to select the correct subentry */ if (P->version == 2) { - u8 *tmap = ROMPTR(bios, P->data[4]); + u8 *tmap = ROMPTR(dev, P->data[4]); if (!tmap) { NV_DEBUG(dev, "no timing map pointer\n"); return NULL; @@ -140,7 +140,6 @@ nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_level *perflvl) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; u8 *vmap; int id; @@ -165,7 +164,7 @@ nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, return; } - vmap = ROMPTR(bios, P->data[32]); + vmap = ROMPTR(dev, P->data[32]); if (!vmap) { NV_DEBUG(dev, "volt map table pointer invalid\n"); return; @@ -200,12 +199,14 @@ nouveau_perf_init(struct drm_device *dev) return; } - perf = ROMPTR(bios, P.data[0]); + perf = ROMPTR(dev, P.data[0]); version = perf[0]; headerlen = perf[1]; if (version < 0x40) { recordlen = perf[3] + (perf[4] * perf[5]); entries = perf[2]; + + pm->pwm_divisor = ROM16(perf[6]); } else { recordlen = perf[2] + (perf[3] * perf[4]); entries = perf[5]; @@ -216,7 +217,7 @@ nouveau_perf_init(struct drm_device *dev) return; } - perf = ROMPTR(bios, bios->data[bios->offset + 0x94]); + perf = ROMPTR(dev, bios->data[bios->offset + 0x94]); if (!perf) { NV_DEBUG(dev, "perf table pointer invalid\n"); return; @@ -283,7 +284,6 @@ nouveau_perf_init(struct drm_device *dev) perflvl->memory = ROM16(entry[11]) * 1000; else perflvl->memory = ROM16(entry[11]) * 2000; - break; case 0x25: perflvl->fanspeed = entry[4]; @@ -300,8 +300,8 @@ nouveau_perf_init(struct drm_device *dev) perflvl->core = ROM16(entry[8]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; - /*XXX: confirm on 0x35 */ - perflvl->unk05 = ROM16(entry[16]) * 1000; + perflvl->vdec = ROM16(entry[16]) * 1000; + perflvl->dom6 = ROM16(entry[20]) * 1000; break; case 0x40: #define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000 diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index a539fd257921..aba3362d421a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -26,6 +26,7 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" +#include "nouveau_gpio.h" #ifdef CONFIG_ACPI #include <linux/acpi.h> @@ -35,22 +36,95 @@ #include <linux/hwmon-sysfs.h> static int -nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u8 id, u32 khz) +nouveau_pwmfan_get(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - void *pre_state; + struct gpio_func gpio; + u32 divs, duty; + int ret; - if (khz == 0) - return 0; + if (!pm->pwm_get) + return -ENODEV; + + ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); + if (ret == 0) { + ret = pm->pwm_get(dev, gpio.line, &divs, &duty); + if (ret == 0) { + divs = max(divs, duty); + if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) + duty = divs - duty; + return (duty * 100) / divs; + } + + return nouveau_gpio_func_get(dev, gpio.func) * 100; + } + + return -ENODEV; +} + +static int +nouveau_pwmfan_set(struct drm_device *dev, int percent) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct gpio_func gpio; + u32 divs, duty; + int ret; + + if (!pm->pwm_set) + return -ENODEV; + + ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); + if (ret == 0) { + divs = pm->pwm_divisor; + if (pm->fan.pwm_freq) { + /*XXX: PNVIO clock more than likely... */ + divs = 135000 / pm->fan.pwm_freq; + if (dev_priv->chipset < 0xa3) + divs /= 4; + } + + duty = ((divs * percent) + 99) / 100; + if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) + duty = divs - duty; - pre_state = pm->clock_pre(dev, perflvl, id, khz); - if (IS_ERR(pre_state)) - return PTR_ERR(pre_state); + return pm->pwm_set(dev, gpio.line, divs, duty); + } + + return -ENODEV; +} + +static int +nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl, + struct nouveau_pm_level *a, struct nouveau_pm_level *b) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + int ret; + + /*XXX: not on all boards, we should control based on temperature + * on recent boards.. or maybe on some other factor we don't + * know about? + */ + if (a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) { + ret = nouveau_pwmfan_set(dev, perflvl->fanspeed); + if (ret && ret != -ENODEV) { + NV_ERROR(dev, "fanspeed set failed: %d\n", ret); + return ret; + } + } + + if (pm->voltage.supported && pm->voltage_set) { + if (perflvl->volt_min && b->volt_min > a->volt_min) { + ret = pm->voltage_set(dev, perflvl->volt_min); + if (ret) { + NV_ERROR(dev, "voltage set failed: %d\n", ret); + return ret; + } + } + } - if (pre_state) - pm->clock_set(dev, pre_state); return 0; } @@ -59,31 +133,24 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + void *state; int ret; if (perflvl == pm->cur) return 0; - if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) { - ret = pm->voltage_set(dev, perflvl->volt_min); - if (ret) { - NV_ERROR(dev, "voltage_set %d failed: %d\n", - perflvl->volt_min, ret); - } - } + ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl); + if (ret) + return ret; - if (pm->clocks_pre) { - void *state = pm->clocks_pre(dev, perflvl); - if (IS_ERR(state)) - return PTR_ERR(state); - pm->clocks_set(dev, state); - } else - if (pm->clock_set) { - nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); - nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); - nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); - nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); - } + state = pm->clocks_pre(dev, perflvl); + if (IS_ERR(state)) + return PTR_ERR(state); + pm->clocks_set(dev, state); + + ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur); + if (ret) + return ret; pm->cur = perflvl; return 0; @@ -130,28 +197,9 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) memset(perflvl, 0, sizeof(*perflvl)); - if (pm->clocks_get) { - ret = pm->clocks_get(dev, perflvl); - if (ret) - return ret; - } else - if (pm->clock_get) { - ret = pm->clock_get(dev, PLL_CORE); - if (ret > 0) - perflvl->core = ret; - - ret = pm->clock_get(dev, PLL_MEMORY); - if (ret > 0) - perflvl->memory = ret; - - ret = pm->clock_get(dev, PLL_SHADER); - if (ret > 0) - perflvl->shader = ret; - - ret = pm->clock_get(dev, PLL_UNK05); - if (ret > 0) - perflvl->unk05 = ret; - } + ret = pm->clocks_get(dev, perflvl); + if (ret) + return ret; if (pm->voltage.supported && pm->voltage_get) { ret = pm->voltage_get(dev); @@ -161,6 +209,10 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + ret = nouveau_pwmfan_get(dev); + if (ret > 0) + perflvl->fanspeed = ret; + return 0; } @@ -412,6 +464,172 @@ static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, nouveau_hwmon_show_update_rate, NULL, 0); +static ssize_t +nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + struct gpio_func gpio; + u32 cycles, cur, prev; + u64 start; + int ret; + + ret = nouveau_gpio_find(dev, 0, DCB_GPIO_FAN_SENSE, 0xff, &gpio); + if (ret) + return ret; + + /* Monitor the GPIO input 0x3b for 250ms. + * When the fan spins, it changes the value of GPIO FAN_SENSE. + * We get 4 changes (0 -> 1 -> 0 -> 1 -> [...]) per complete rotation. + */ + start = ptimer->read(dev); + prev = nouveau_gpio_sense(dev, 0, gpio.line); + cycles = 0; + do { + cur = nouveau_gpio_sense(dev, 0, gpio.line); + if (prev != cur) { + cycles++; + prev = cur; + } + + usleep_range(500, 1000); /* supports 0 < rpm < 7500 */ + } while (ptimer->read(dev) - start < 250000000); + + /* interpolate to get rpm */ + return sprintf(buf, "%i\n", cycles / 4 * 4 * 60); +} +static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input, + NULL, 0); + +static ssize_t +nouveau_hwmon_get_pwm0(struct device *d, struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + int ret; + + ret = nouveau_pwmfan_get(dev); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t +nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + int ret = -ENODEV; + long value; + + if (nouveau_perflvl_wr != 7777) + return -EPERM; + + if (strict_strtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + if (value < pm->fan.min_duty) + value = pm->fan.min_duty; + if (value > pm->fan.max_duty) + value = pm->fan.max_duty; + + ret = nouveau_pwmfan_set(dev, value); + if (ret) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm0, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm0, + nouveau_hwmon_set_pwm0, 0); + +static ssize_t +nouveau_hwmon_get_pwm0_min(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + + return sprintf(buf, "%i\n", pm->fan.min_duty); +} + +static ssize_t +nouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + long value; + + if (strict_strtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + if (value < 0) + value = 0; + + if (pm->fan.max_duty - value < 10) + value = pm->fan.max_duty - 10; + + if (value < 10) + pm->fan.min_duty = 10; + else + pm->fan.min_duty = value; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm0_min, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm0_min, + nouveau_hwmon_set_pwm0_min, 0); + +static ssize_t +nouveau_hwmon_get_pwm0_max(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + + return sprintf(buf, "%i\n", pm->fan.max_duty); +} + +static ssize_t +nouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + long value; + + if (strict_strtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + if (value < 0) + value = 0; + + if (value - pm->fan.min_duty < 10) + value = pm->fan.min_duty + 10; + + if (value > 100) + pm->fan.max_duty = 100; + else + pm->fan.max_duty = value; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm0_max, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm0_max, + nouveau_hwmon_set_pwm0_max, 0); + static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -420,10 +638,26 @@ static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_update_rate.dev_attr.attr, NULL }; +static struct attribute *hwmon_fan_rpm_attributes[] = { + &sensor_dev_attr_fan0_input.dev_attr.attr, + NULL +}; +static struct attribute *hwmon_pwm_fan_attributes[] = { + &sensor_dev_attr_pwm0.dev_attr.attr, + &sensor_dev_attr_pwm0_min.dev_attr.attr, + &sensor_dev_attr_pwm0_max.dev_attr.attr, + NULL +}; static const struct attribute_group hwmon_attrgroup = { .attrs = hwmon_attributes, }; +static const struct attribute_group hwmon_fan_rpm_attrgroup = { + .attrs = hwmon_fan_rpm_attributes, +}; +static const struct attribute_group hwmon_pwm_fan_attrgroup = { + .attrs = hwmon_pwm_fan_attributes, +}; #endif static int @@ -433,7 +667,7 @@ nouveau_hwmon_init(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *hwmon_dev; - int ret; + int ret = 0; if (!pm->temp_get) return -ENODEV; @@ -446,17 +680,46 @@ nouveau_hwmon_init(struct drm_device *dev) return ret; } dev_set_drvdata(hwmon_dev, dev); + + /* default sysfs entries */ ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); if (ret) { - NV_ERROR(dev, - "Unable to create hwmon sysfs file: %d\n", ret); - hwmon_device_unregister(hwmon_dev); - return ret; + if (ret) + goto error; + } + + /* if the card has a pwm fan */ + /*XXX: incorrect, need better detection for this, some boards have + * the gpio entries for pwm fan control even when there's no + * actual fan connected to it... therm table? */ + if (nouveau_pwmfan_get(dev) >= 0) { + ret = sysfs_create_group(&dev->pdev->dev.kobj, + &hwmon_pwm_fan_attrgroup); + if (ret) + goto error; + } + + /* if the card can read the fan rpm */ + if (nouveau_gpio_func_valid(dev, DCB_GPIO_FAN_SENSE)) { + ret = sysfs_create_group(&dev->pdev->dev.kobj, + &hwmon_fan_rpm_attrgroup); + if (ret) + goto error; } pm->hwmon = hwmon_dev; -#endif + + return 0; + +error: + NV_ERROR(dev, "Unable to create some hwmon sysfs files: %d\n", ret); + hwmon_device_unregister(hwmon_dev); + pm->hwmon = NULL; + return ret; +#else + pm->hwmon = NULL; return 0; +#endif } static void @@ -468,6 +731,9 @@ nouveau_hwmon_fini(struct drm_device *dev) if (pm->hwmon) { sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); + sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup); + sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup); + hwmon_device_unregister(pm->hwmon); } #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 8ac02cdd03a1..2f8e14fbcff8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -47,29 +47,33 @@ void nouveau_mem_timing_init(struct drm_device *); void nouveau_mem_timing_fini(struct drm_device *); /* nv04_pm.c */ -int nv04_pm_clock_get(struct drm_device *, u32 id); -void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, - u32 id, int khz); -void nv04_pm_clock_set(struct drm_device *, void *); +int nv04_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nv04_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +int nv04_pm_clocks_set(struct drm_device *, void *); /* nv40_pm.c */ int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); -void nv40_pm_clocks_set(struct drm_device *, void *); +int nv40_pm_clocks_set(struct drm_device *, void *); +int nv40_pm_pwm_get(struct drm_device *, int, u32 *, u32 *); +int nv40_pm_pwm_set(struct drm_device *, int, u32, u32); /* nv50_pm.c */ -int nv50_pm_clock_get(struct drm_device *, u32 id); -void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, - u32 id, int khz); -void nv50_pm_clock_set(struct drm_device *, void *); +int nv50_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nv50_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +int nv50_pm_clocks_set(struct drm_device *, void *); +int nv50_pm_pwm_get(struct drm_device *, int, u32 *, u32 *); +int nv50_pm_pwm_set(struct drm_device *, int, u32, u32); /* nva3_pm.c */ int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); -void nva3_pm_clocks_set(struct drm_device *, void *); +int nva3_pm_clocks_set(struct drm_device *, void *); /* nvc0_pm.c */ int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nvc0_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +int nvc0_pm_clocks_set(struct drm_device *, void *); /* nouveau_temp.c */ void nouveau_temp_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index d8831ab42bb9..f5e98910d17f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -36,6 +36,7 @@ #include "nouveau_drm.h" #include "nouveau_fbcon.h" #include "nouveau_ramht.h" +#include "nouveau_gpio.h" #include "nouveau_pm.h" #include "nv50_display.h" @@ -80,16 +81,12 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = NULL; - engine->gpio.set = NULL; - engine->gpio.irq_enable = NULL; - engine->pm.clock_get = nv04_pm_clock_get; - engine->pm.clock_pre = nv04_pm_clock_pre; - engine->pm.clock_set = nv04_pm_clock_set; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; + engine->pm.clocks_get = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; @@ -129,16 +126,14 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; - engine->pm.clock_get = nv04_pm_clock_get; - engine->pm.clock_pre = nv04_pm_clock_pre; - engine->pm.clock_set = nv04_pm_clock_set; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; + engine->pm.clocks_get = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; @@ -178,16 +173,14 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; - engine->pm.clock_get = nv04_pm_clock_get; - engine->pm.clock_pre = nv04_pm_clock_pre; - engine->pm.clock_set = nv04_pm_clock_set; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; + engine->pm.clocks_get = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; @@ -227,16 +220,14 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; - engine->pm.clock_get = nv04_pm_clock_get; - engine->pm.clock_pre = nv04_pm_clock_pre; - engine->pm.clock_set = nv04_pm_clock_set; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; + engine->pm.clocks_get = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->vram.init = nouveau_mem_detect; @@ -279,19 +270,22 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; + engine->gpio.init = nv10_gpio_init; + engine->gpio.fini = nv10_gpio_fini; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; + engine->gpio.irq_enable = nv10_gpio_irq_enable; engine->pm.clocks_get = nv40_pm_clocks_get; engine->pm.clocks_pre = nv40_pm_clocks_pre; engine->pm.clocks_set = nv40_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->pm.temp_get = nv40_temp_get; + engine->pm.pwm_get = nv40_pm_pwm_get; + engine->pm.pwm_set = nv40_pm_pwm_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; @@ -334,14 +328,13 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; - engine->display.init = nv50_display_init; engine->display.destroy = nv50_display_destroy; + engine->display.init = nv50_display_init; + engine->display.fini = nv50_display_fini; engine->gpio.init = nv50_gpio_init; - engine->gpio.takedown = nv50_gpio_fini; - engine->gpio.get = nv50_gpio_get; - engine->gpio.set = nv50_gpio_set; - engine->gpio.irq_register = nv50_gpio_irq_register; - engine->gpio.irq_unregister = nv50_gpio_irq_unregister; + engine->gpio.fini = nv50_gpio_fini; + engine->gpio.drive = nv50_gpio_drive; + engine->gpio.sense = nv50_gpio_sense; engine->gpio.irq_enable = nv50_gpio_irq_enable; switch (dev_priv->chipset) { case 0x84: @@ -354,9 +347,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) case 0xaa: case 0xac: case 0x50: - engine->pm.clock_get = nv50_pm_clock_get; - engine->pm.clock_pre = nv50_pm_clock_pre; - engine->pm.clock_set = nv50_pm_clock_set; + engine->pm.clocks_get = nv50_pm_clocks_get; + engine->pm.clocks_pre = nv50_pm_clocks_pre; + engine->pm.clocks_set = nv50_pm_clocks_set; break; default: engine->pm.clocks_get = nva3_pm_clocks_get; @@ -370,6 +363,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv84_temp_get; else engine->pm.temp_get = nv40_temp_get; + engine->pm.pwm_get = nv50_pm_pwm_get; + engine->pm.pwm_set = nv50_pm_pwm_set; engine->vram.init = nv50_vram_init; engine->vram.takedown = nv50_vram_fini; engine->vram.get = nv50_vram_new; @@ -407,14 +402,13 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; - engine->display.init = nv50_display_init; engine->display.destroy = nv50_display_destroy; + engine->display.init = nv50_display_init; + engine->display.fini = nv50_display_fini; engine->gpio.init = nv50_gpio_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv50_gpio_get; - engine->gpio.set = nv50_gpio_set; - engine->gpio.irq_register = nv50_gpio_irq_register; - engine->gpio.irq_unregister = nv50_gpio_irq_unregister; + engine->gpio.fini = nv50_gpio_fini; + engine->gpio.drive = nv50_gpio_drive; + engine->gpio.sense = nv50_gpio_sense; engine->gpio.irq_enable = nv50_gpio_irq_enable; engine->vram.init = nvc0_vram_init; engine->vram.takedown = nv50_vram_fini; @@ -423,8 +417,12 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->vram.flags_valid = nvc0_vram_flags_valid; engine->pm.temp_get = nv84_temp_get; engine->pm.clocks_get = nvc0_pm_clocks_get; + engine->pm.clocks_pre = nvc0_pm_clocks_pre; + engine->pm.clocks_set = nvc0_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; + engine->pm.pwm_get = nv50_pm_pwm_get; + engine->pm.pwm_set = nv50_pm_pwm_set; break; case 0xd0: engine->instmem.init = nvc0_instmem_init; @@ -457,21 +455,23 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nouveau_stub_init; engine->display.late_takedown = nouveau_stub_takedown; engine->display.create = nvd0_display_create; - engine->display.init = nvd0_display_init; engine->display.destroy = nvd0_display_destroy; + engine->display.init = nvd0_display_init; + engine->display.fini = nvd0_display_fini; engine->gpio.init = nv50_gpio_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nvd0_gpio_get; - engine->gpio.set = nvd0_gpio_set; - engine->gpio.irq_register = nv50_gpio_irq_register; - engine->gpio.irq_unregister = nv50_gpio_irq_unregister; + engine->gpio.fini = nv50_gpio_fini; + engine->gpio.drive = nvd0_gpio_drive; + engine->gpio.sense = nvd0_gpio_sense; engine->gpio.irq_enable = nv50_gpio_irq_enable; engine->vram.init = nvc0_vram_init; engine->vram.takedown = nv50_vram_fini; engine->vram.get = nvc0_vram_new; engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nvc0_vram_flags_valid; + engine->pm.temp_get = nv84_temp_get; engine->pm.clocks_get = nvc0_pm_clocks_get; + engine->pm.clocks_pre = nvc0_pm_clocks_pre; + engine->pm.clocks_set = nvc0_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; break; @@ -615,7 +615,7 @@ nouveau_card_init(struct drm_device *dev) goto out_gart; /* PGPIO */ - ret = engine->gpio.init(dev); + ret = nouveau_gpio_create(dev); if (ret) goto out_mc; @@ -648,6 +648,7 @@ nouveau_card_init(struct drm_device *dev) nv50_graph_create(dev); break; case NV_C0: + case NV_D0: nvc0_graph_create(dev); break; default: @@ -663,6 +664,11 @@ nouveau_card_init(struct drm_device *dev) case 0xa0: nv84_crypt_create(dev); break; + case 0x98: + case 0xaa: + case 0xac: + nv98_crypt_create(dev); + break; } switch (dev_priv->card_type) { @@ -684,15 +690,25 @@ nouveau_card_init(struct drm_device *dev) break; } + if (dev_priv->chipset >= 0xa3 || dev_priv->chipset == 0x98) { + nv84_bsp_create(dev); + nv84_vp_create(dev); + nv98_ppp_create(dev); + } else + if (dev_priv->chipset >= 0x84) { + nv50_mpeg_create(dev); + nv84_bsp_create(dev); + nv84_vp_create(dev); + } else + if (dev_priv->chipset >= 0x50) { + nv50_mpeg_create(dev); + } else if (dev_priv->card_type == NV_40 || dev_priv->chipset == 0x31 || dev_priv->chipset == 0x34 || - dev_priv->chipset == 0x36) + dev_priv->chipset == 0x36) { nv31_mpeg_create(dev); - else - if (dev_priv->card_type == NV_50 && - (dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0)) - nv50_mpeg_create(dev); + } for (e = 0; e < NVOBJ_ENGINE_NR; e++) { if (dev_priv->eng[e]) { @@ -712,27 +728,7 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_fifo; - /* initialise general modesetting */ - drm_mode_config_init(dev); - drm_mode_create_scaling_mode_property(dev); - drm_mode_create_dithering_property(dev); - dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; - dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - if (dev_priv->card_type < NV_10) { - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; - } else - if (dev_priv->card_type < NV_50) { - dev->mode_config.max_width = 4096; - dev->mode_config.max_height = 4096; - } else { - dev->mode_config.max_width = 8192; - dev->mode_config.max_height = 8192; - } - - ret = engine->display.create(dev); + ret = nouveau_display_create(dev); if (ret) goto out_irq; @@ -752,12 +748,11 @@ nouveau_card_init(struct drm_device *dev) } if (dev->mode_config.num_crtc) { - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + ret = nouveau_display_init(dev); if (ret) goto out_chan; nouveau_fbcon_init(dev); - drm_kms_helper_poll_init(dev); } return 0; @@ -768,7 +763,7 @@ out_fence: nouveau_fence_fini(dev); out_disp: nouveau_backlight_exit(dev); - engine->display.destroy(dev); + nouveau_display_destroy(dev); out_irq: nouveau_irq_fini(dev); out_fifo: @@ -788,7 +783,7 @@ out_engine: out_timer: engine->timer.takedown(dev); out_gpio: - engine->gpio.takedown(dev); + nouveau_gpio_destroy(dev); out_mc: engine->mc.takedown(dev); out_gart: @@ -818,9 +813,8 @@ static void nouveau_card_takedown(struct drm_device *dev) int e; if (dev->mode_config.num_crtc) { - drm_kms_helper_poll_fini(dev); nouveau_fbcon_fini(dev); - drm_vblank_cleanup(dev); + nouveau_display_fini(dev); } if (dev_priv->channel) { @@ -829,8 +823,7 @@ static void nouveau_card_takedown(struct drm_device *dev) } nouveau_backlight_exit(dev); - engine->display.destroy(dev); - drm_mode_config_cleanup(dev); + nouveau_display_destroy(dev); if (!dev_priv->noaccel) { engine->fifo.takedown(dev); @@ -843,7 +836,7 @@ static void nouveau_card_takedown(struct drm_device *dev) } engine->fb.takedown(dev); engine->timer.takedown(dev); - engine->gpio.takedown(dev); + nouveau_gpio_destroy(dev); engine->mc.takedown(dev); engine->display.late_takedown(dev); @@ -1110,13 +1103,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) dev_priv->noaccel = !!nouveau_noaccel; if (nouveau_noaccel == -1) { switch (dev_priv->chipset) { -#if 0 - case 0xXX: /* known broken */ + case 0xd9: /* known broken */ NV_INFO(dev, "acceleration disabled by default, pass " "noaccel=0 to force enable\n"); dev_priv->noaccel = true; break; -#endif default: dev_priv->noaccel = false; break; @@ -1238,7 +1229,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, getparam->value = 1; break; case NOUVEAU_GETPARAM_HAS_PAGEFLIP: - getparam->value = dev_priv->card_type < NV_D0; + getparam->value = 1; break; case NOUVEAU_GETPARAM_GRAPH_UNITS: /* NV40 and NV50 versions are quite different, but register diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 5a46446dd5a8..0f5a30160556 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -55,6 +55,10 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) temps->down_clock = 100; temps->fan_boost = 90; + /* Set the default range for the pwm fan */ + pm->fan.min_duty = 30; + pm->fan.max_duty = 100; + /* Set the known default values to setup the temperature sensor */ if (dev_priv->card_type >= NV_40) { switch (dev_priv->chipset) { @@ -156,11 +160,26 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) case 0x13: sensor->slope_div = value; break; + case 0x22: + pm->fan.min_duty = value & 0xff; + pm->fan.max_duty = (value & 0xff00) >> 8; + break; + case 0x26: + pm->fan.pwm_freq = value; + break; } temp += recordlen; } nouveau_temp_safety_checks(dev); + + /* check the fan min/max settings */ + if (pm->fan.min_duty < 10) + pm->fan.min_duty = 10; + if (pm->fan.max_duty > 100) + pm->fan.max_duty = 100; + if (pm->fan.max_duty < pm->fan.min_duty) + pm->fan.max_duty = pm->fan.min_duty; } static int @@ -267,8 +286,6 @@ probe_monitoring_device(struct nouveau_i2c_chan *i2c, static void nouveau_temp_probe_i2c(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_table *dcb = &dev_priv->vbios.dcb; struct i2c_board_info info[] = { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, { I2C_BOARD_INFO("w83781d", 0x2d) }, @@ -277,11 +294,9 @@ nouveau_temp_probe_i2c(struct drm_device *dev) { I2C_BOARD_INFO("lm99", 0x4c) }, { } }; - int idx = (dcb->version >= 0x40 ? - dcb->i2c_default_indices & 0xf : 2); nouveau_i2c_identify(dev, "monitoring device", info, - probe_monitoring_device, idx); + probe_monitoring_device, NV_I2C_DEFAULT(0)); } void @@ -297,9 +312,9 @@ nouveau_temp_init(struct drm_device *dev) return; if (P.version == 1) - temp = ROMPTR(bios, P.data[12]); + temp = ROMPTR(dev, P.data[12]); else if (P.version == 2) - temp = ROMPTR(bios, P.data[16]); + temp = ROMPTR(dev, P.data[16]); else NV_WARN(dev, "unknown temp for BIT P %d\n", P.version); diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c index ef0832b29ad2..2bf6c0350b4b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vm.c @@ -78,9 +78,10 @@ nouveau_vm_map(struct nouveau_vma *vma, struct nouveau_mem *node) void nouveau_vm_map_sg(struct nouveau_vma *vma, u64 delta, u64 length, - struct nouveau_mem *mem, dma_addr_t *list) + struct nouveau_mem *mem) { struct nouveau_vm *vm = vma->vm; + dma_addr_t *list = mem->pages; int big = vma->node->type != vm->spg_shift; u32 offset = vma->node->offset + (delta >> 12); u32 bits = vma->node->type - 12; diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h index 6ce995f7797e..4fb6e728734d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.h +++ b/drivers/gpu/drm/nouveau/nouveau_vm.h @@ -89,7 +89,7 @@ void nouveau_vm_map_at(struct nouveau_vma *, u64 offset, struct nouveau_mem *); void nouveau_vm_unmap(struct nouveau_vma *); void nouveau_vm_unmap_at(struct nouveau_vma *, u64 offset, u64 length); void nouveau_vm_map_sg(struct nouveau_vma *, u64 offset, u64 length, - struct nouveau_mem *, dma_addr_t *); + struct nouveau_mem *); /* nv50_vm.c */ void nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 86d03e15735d..b010cb997b34 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -26,6 +26,7 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" +#include "nouveau_gpio.h" static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 }; static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]); @@ -34,7 +35,6 @@ int nouveau_voltage_gpio_get(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; u8 vid = 0; int i; @@ -43,7 +43,7 @@ nouveau_voltage_gpio_get(struct drm_device *dev) if (!(volt->vid_mask & (1 << i))) continue; - vid |= gpio->get(dev, vidtag[i]) << i; + vid |= nouveau_gpio_func_get(dev, vidtag[i]) << i; } return nouveau_volt_lvl_lookup(dev, vid); @@ -53,7 +53,6 @@ int nouveau_voltage_gpio_set(struct drm_device *dev, int voltage) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; int vid, i; @@ -65,7 +64,7 @@ nouveau_voltage_gpio_set(struct drm_device *dev, int voltage) if (!(volt->vid_mask & (1 << i))) continue; - gpio->set(dev, vidtag[i], !!(vid & (1 << i))); + nouveau_gpio_func_set(dev, vidtag[i], !!(vid & (1 << i))); } return 0; @@ -117,10 +116,10 @@ nouveau_volt_init(struct drm_device *dev) return; if (P.version == 1) - volt = ROMPTR(bios, P.data[16]); + volt = ROMPTR(dev, P.data[16]); else if (P.version == 2) - volt = ROMPTR(bios, P.data[12]); + volt = ROMPTR(dev, P.data[12]); else { NV_WARN(dev, "unknown volt for BIT P %d\n", P.version); } @@ -130,7 +129,7 @@ nouveau_volt_init(struct drm_device *dev) return; } - volt = ROMPTR(bios, bios->data[bios->offset + 0x98]); + volt = ROMPTR(dev, bios->data[bios->offset + 0x98]); } if (!volt) { @@ -194,7 +193,7 @@ nouveau_volt_init(struct drm_device *dev) return; } - if (!nouveau_bios_gpio_entry(dev, vidtag[i])) { + if (!nouveau_gpio_func_valid(dev, vidtag[i])) { NV_DEBUG(dev, "vid bit %d has no gpio tag\n", i); return; } diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c index e000455e06d0..8300266ffaea 100644 --- a/drivers/gpu/drm/nouveau/nv04_dac.c +++ b/drivers/gpu/drm/nouveau/nv04_dac.c @@ -32,6 +32,7 @@ #include "nouveau_connector.h" #include "nouveau_crtc.h" #include "nouveau_hw.h" +#include "nouveau_gpio.h" #include "nvreg.h" int nv04_dac_output_offset(struct drm_encoder *encoder) @@ -220,7 +221,6 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, @@ -252,11 +252,11 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); } - saved_gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1); - saved_gpio0 = gpio->get(dev, DCB_GPIO_TVDAC0); + saved_gpio1 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC1); + saved_gpio0 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC0); - gpio->set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); - gpio->set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); msleep(4); @@ -306,8 +306,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); - gpio->set(dev, DCB_GPIO_TVDAC1, saved_gpio1); - gpio->set(dev, DCB_GPIO_TVDAC0, saved_gpio0); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); return sample; } diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index 12098bf839c4..2258746016f8 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -289,6 +289,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_display_mode *output_mode = &nv_encoder->mode; + struct drm_connector *connector = &nv_connector->base; uint32_t mode_ratio, panel_ratio; NV_DEBUG_KMS(dev, "Output mode on CRTC %d:\n", nv_crtc->index); @@ -340,10 +341,15 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, output_mode->clock > 165000) regp->fp_control |= (2 << 24); if (nv_encoder->dcb->type == OUTPUT_LVDS) { - bool duallink, dummy; + bool duallink = false, dummy; + if (nv_connector->edid && + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { + duallink = (((u8 *)nv_connector->edid)[121] == 2); + } else { + nouveau_bios_parse_lvds_table(dev, output_mode->clock, + &duallink, &dummy); + } - nouveau_bios_parse_lvds_table(dev, output_mode->clock, - &duallink, &dummy); if (duallink) regp->fp_control |= (8 << 28); } else @@ -407,7 +413,9 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, } /* Output property. */ - if (nv_connector->use_dithering) { + if ((nv_connector->dithering_mode == DITHERING_MODE_ON) || + (nv_connector->dithering_mode == DITHERING_MODE_AUTO && + encoder->crtc->fb->depth > connector->display_info.bpc * 3)) { if (dev_priv->chipset == 0x11) regp->dither = savep->dither | 0x00010000; else { diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 6bd8518d7b2e..7047d37e8dab 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -243,6 +243,11 @@ nv04_display_init(struct drm_device *dev) return 0; } +void +nv04_display_fini(struct drm_device *dev) +{ +} + static void nv04_vblank_crtc0_isr(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c index 9ae92a87b8cc..6e7589918fa9 100644 --- a/drivers/gpu/drm/nouveau/nv04_pm.c +++ b/drivers/gpu/drm/nouveau/nv04_pm.c @@ -27,68 +27,111 @@ #include "nouveau_hw.h" #include "nouveau_pm.h" -struct nv04_pm_state { +int +nv04_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + int ret; + + ret = nouveau_hw_get_clock(dev, PLL_CORE); + if (ret < 0) + return ret; + perflvl->core = ret; + + ret = nouveau_hw_get_clock(dev, PLL_MEMORY); + if (ret < 0) + return ret; + perflvl->memory = ret; + + return 0; +} + +struct nv04_pm_clock { struct pll_lims pll; struct nouveau_pll_vals calc; }; -int -nv04_pm_clock_get(struct drm_device *dev, u32 id) +struct nv04_pm_state { + struct nv04_pm_clock core; + struct nv04_pm_clock memory; +}; + +static int +calc_pll(struct drm_device *dev, u32 id, int khz, struct nv04_pm_clock *clk) { - return nouveau_hw_get_clock(dev, id); + int ret; + + ret = get_pll_limits(dev, id, &clk->pll); + if (ret) + return ret; + + ret = nouveau_calc_pll_mnp(dev, &clk->pll, khz, &clk->calc); + if (!ret) + return -EINVAL; + + return 0; } void * -nv04_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u32 id, int khz) +nv04_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct nv04_pm_state *state; + struct nv04_pm_state *info; int ret; - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) return ERR_PTR(-ENOMEM); - ret = get_pll_limits(dev, id, &state->pll); - if (ret) { - kfree(state); - return (ret == -ENOENT) ? NULL : ERR_PTR(ret); - } + ret = calc_pll(dev, PLL_CORE, perflvl->core, &info->core); + if (ret) + goto error; - ret = nouveau_calc_pll_mnp(dev, &state->pll, khz, &state->calc); - if (!ret) { - kfree(state); - return ERR_PTR(-EINVAL); + if (perflvl->memory) { + ret = calc_pll(dev, PLL_MEMORY, perflvl->memory, &info->memory); + if (ret) + goto error; } - return state; + return info; +error: + kfree(info); + return ERR_PTR(ret); } -void -nv04_pm_clock_set(struct drm_device *dev, void *pre_state) +static void +prog_pll(struct drm_device *dev, struct nv04_pm_clock *clk) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; - struct nv04_pm_state *state = pre_state; - u32 reg = state->pll.reg; + u32 reg = clk->pll.reg; /* thank the insane nouveau_hw_setpll() interface for this */ if (dev_priv->card_type >= NV_40) reg += 4; - nouveau_hw_setpll(dev, reg, &state->calc); + nouveau_hw_setpll(dev, reg, &clk->calc); +} + +int +nv04_pm_clocks_set(struct drm_device *dev, void *pre_state) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + struct nv04_pm_state *state = pre_state; + + prog_pll(dev, &state->core); - if (dev_priv->card_type < NV_30 && reg == NV_PRAMDAC_MPLL_COEFF) { - if (dev_priv->card_type == NV_20) - nv_mask(dev, 0x1002c4, 0, 1 << 20); + if (state->memory.pll.reg) { + prog_pll(dev, &state->memory); + if (dev_priv->card_type < NV_30) { + if (dev_priv->card_type == NV_20) + nv_mask(dev, 0x1002c4, 0, 1 << 20); - /* Reset the DLLs */ - nv_mask(dev, 0x1002c0, 0, 1 << 8); + /* Reset the DLLs */ + nv_mask(dev, 0x1002c0, 0, 1 << 8); + } } - if (reg == NV_PRAMDAC_NVPLL_COEFF) - ptimer->init(dev); + ptimer->init(dev); kfree(state); + return 0; } - diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c index 263301b809dd..55c945290e52 100644 --- a/drivers/gpu/drm/nouveau/nv04_timer.c +++ b/drivers/gpu/drm/nouveau/nv04_timer.c @@ -2,6 +2,7 @@ #include "drm.h" #include "nouveau_drv.h" #include "nouveau_drm.h" +#include "nouveau_hw.h" int nv04_timer_init(struct drm_device *dev) @@ -17,7 +18,7 @@ nv04_timer_init(struct drm_device *dev) /* determine base clock for timer source */ if (dev_priv->chipset < 0x40) { - n = dev_priv->engine.pm.clock_get(dev, PLL_CORE); + n = nouveau_hw_get_clock(dev, PLL_CORE); } else if (dev_priv->chipset == 0x40) { /*XXX: figure this out */ diff --git a/drivers/gpu/drm/nouveau/nv10_gpio.c b/drivers/gpu/drm/nouveau/nv10_gpio.c index 007fc29e2f86..550ad3fcf0af 100644 --- a/drivers/gpu/drm/nouveau/nv10_gpio.c +++ b/drivers/gpu/drm/nouveau/nv10_gpio.c @@ -27,66 +27,97 @@ #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_hw.h" +#include "nouveau_gpio.h" -static bool -get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift, - uint32_t *mask) +int +nv10_gpio_sense(struct drm_device *dev, int line) { - if (ent->line < 2) { - *reg = NV_PCRTC_GPIO; - *shift = ent->line * 16; - *mask = 0x11; - - } else if (ent->line < 10) { - *reg = NV_PCRTC_GPIO_EXT; - *shift = (ent->line - 2) * 4; - *mask = 0x3; + if (line < 2) { + line = line * 16; + line = NVReadCRTC(dev, 0, NV_PCRTC_GPIO) >> line; + return !!(line & 0x0100); + } else + if (line < 10) { + line = (line - 2) * 4; + line = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT) >> line; + return !!(line & 0x04); + } else + if (line < 14) { + line = (line - 10) * 4; + line = NVReadCRTC(dev, 0, NV_PCRTC_850) >> line; + return !!(line & 0x04); + } - } else if (ent->line < 14) { - *reg = NV_PCRTC_850; - *shift = (ent->line - 10) * 4; - *mask = 0x3; + return -EINVAL; +} +int +nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out) +{ + u32 reg, mask, data; + + if (line < 2) { + line = line * 16; + reg = NV_PCRTC_GPIO; + mask = 0x00000011; + data = (dir << 4) | out; + } else + if (line < 10) { + line = (line - 2) * 4; + reg = NV_PCRTC_GPIO_EXT; + mask = 0x00000003 << ((line - 2) * 4); + data = (dir << 1) | out; + } else + if (line < 14) { + line = (line - 10) * 4; + reg = NV_PCRTC_850; + mask = 0x00000003; + data = (dir << 1) | out; } else { - return false; + return -EINVAL; } - return true; + mask = NVReadCRTC(dev, 0, reg) & ~(mask << line); + NVWriteCRTC(dev, 0, reg, mask | (data << line)); + return 0; } -int -nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +void +nv10_gpio_irq_enable(struct drm_device *dev, int line, bool on) { - struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag); - uint32_t reg, shift, mask, value; + u32 mask = 0x00010001 << line; - if (!ent) - return -ENODEV; + nv_wr32(dev, 0x001104, mask); + nv_mask(dev, 0x001144, mask, on ? mask : 0); +} - if (!get_gpio_location(ent, ®, &shift, &mask)) - return -ENODEV; +static void +nv10_gpio_isr(struct drm_device *dev) +{ + u32 intr = nv_rd32(dev, 0x1104); + u32 hi = (intr & 0x0000ffff) >> 0; + u32 lo = (intr & 0xffff0000) >> 16; - value = NVReadCRTC(dev, 0, reg) >> shift; + nouveau_gpio_isr(dev, 0, hi | lo); - return (ent->invert ? 1 : 0) ^ (value & 1); + nv_wr32(dev, 0x001104, intr); } int -nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +nv10_gpio_init(struct drm_device *dev) { - struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag); - uint32_t reg, shift, mask, value; - - if (!ent) - return -ENODEV; - - if (!get_gpio_location(ent, ®, &shift, &mask)) - return -ENODEV; - - value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift; - mask = ~(mask << shift); - - NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask)); - + nv_wr32(dev, 0x001140, 0x00000000); + nv_wr32(dev, 0x001100, 0xffffffff); + nv_wr32(dev, 0x001144, 0x00000000); + nv_wr32(dev, 0x001104, 0xffffffff); + nouveau_irq_register(dev, 28, nv10_gpio_isr); /* PBUS */ return 0; } + +void +nv10_gpio_fini(struct drm_device *dev) +{ + nv_wr32(dev, 0x001140, 0x00000000); + nv_wr32(dev, 0x001144, 0x00000000); + nouveau_irq_unregister(dev, 28); +} diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 3900cebba560..696d7e7dc2a0 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -30,6 +30,7 @@ #include "nouveau_encoder.h" #include "nouveau_connector.h" #include "nouveau_crtc.h" +#include "nouveau_gpio.h" #include "nouveau_hw.h" #include "nv17_tv.h" @@ -37,7 +38,6 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; uint32_t testval, regoffset = nv04_dac_output_offset(encoder); uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end, fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c; @@ -53,8 +53,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) head = (dacclk & 0x100) >> 8; /* Save the previous state. */ - gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1); - gpio0 = gpio->get(dev, DCB_GPIO_TVDAC0); + gpio1 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC1); + gpio0 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC0); fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL); fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START); fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END); @@ -65,8 +65,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c); /* Prepare the DAC for load detection. */ - gpio->set(dev, DCB_GPIO_TVDAC1, true); - gpio->set(dev, DCB_GPIO_TVDAC0, true); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, true); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, true); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047); @@ -111,8 +111,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal); - gpio->set(dev, DCB_GPIO_TVDAC1, gpio1); - gpio->set(dev, DCB_GPIO_TVDAC0, gpio0); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, gpio1); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, gpio0); return sample; } @@ -357,8 +357,6 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); @@ -383,8 +381,8 @@ static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) nv_load_ptv(dev, regs, 200); - gpio->set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON); - gpio->set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON); nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); } diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index e676b0d53478..c7615381c5d9 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -222,7 +222,7 @@ nv40_pm_gr_idle(void *data) return true; } -void +int nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -231,7 +231,7 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) struct bit_entry M; u32 crtc_mask = 0; u8 sr1[2]; - int i; + int i, ret = -EAGAIN; /* determine which CRTCs are active, fetch VGA_SR1 for each */ for (i = 0; i < 2; i++) { @@ -263,6 +263,8 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) if (!nv_wait_cb(dev, nv40_pm_gr_idle, dev)) goto resume; + ret = 0; + /* set engine clocks */ nv_mask(dev, 0x00c040, 0x00000333, 0x00000000); nv_wr32(dev, 0x004004, info->npll_coef); @@ -345,4 +347,48 @@ resume: spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); kfree(info); + return ret; +} + +int +nv40_pm_pwm_get(struct drm_device *dev, int line, u32 *divs, u32 *duty) +{ + if (line == 2) { + u32 reg = nv_rd32(dev, 0x0010f0); + if (reg & 0x80000000) { + *duty = (reg & 0x7fff0000) >> 16; + *divs = (reg & 0x00007fff); + return 0; + } + } else + if (line == 9) { + u32 reg = nv_rd32(dev, 0x0015f4); + if (reg & 0x80000000) { + *divs = nv_rd32(dev, 0x0015f8); + *duty = (reg & 0x7fffffff); + return 0; + } + } else { + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", line); + return -ENODEV; + } + + return -EINVAL; +} + +int +nv40_pm_pwm_set(struct drm_device *dev, int line, u32 divs, u32 duty) +{ + if (line == 2) { + nv_wr32(dev, 0x0010f0, 0x80000000 | (duty << 16) | divs); + } else + if (line == 9) { + nv_wr32(dev, 0x0015f8, divs); + nv_wr32(dev, 0x0015f4, duty | 0x80000000); + } else { + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", line); + return -ENODEV; + } + + return 0; } diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 882080e0b4f5..8f6c2ace3adf 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -132,33 +132,42 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) } static int -nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) +nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) { - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_channel *evo = nv50_display(dev)->master; - int ret; - - NV_DEBUG_KMS(dev, "\n"); + struct nouveau_channel *evo = nv50_display(nv_crtc->base.dev)->master; + struct nouveau_connector *nv_connector; + struct drm_connector *connector; + int head = nv_crtc->index, ret; + u32 mode = 0x00; - ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); - if (ret) { - NV_ERROR(dev, "no space while setting dither\n"); - return ret; + nv_connector = nouveau_crtc_connector_get(nv_crtc); + connector = &nv_connector->base; + if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { + if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = nv_connector->dithering_mode; } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1); - if (on) - OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON); - else - OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF); + if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { + if (connector->display_info.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= nv_connector->dithering_depth; + } - if (update) { - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING(evo, 0); - FIRE_RING(evo); + ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); + if (ret == 0) { + BEGIN_RING(evo, 0, NV50_EVO_CRTC(head, DITHER_CTRL), 1); + OUT_RING (evo, mode); + if (update) { + BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + OUT_RING (evo, 0); + FIRE_RING (evo); + } } - return 0; + return ret; } struct nouveau_connector * @@ -180,80 +189,103 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) } static int -nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) +nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) { - struct nouveau_connector *nv_connector = - nouveau_crtc_connector_get(nv_crtc); - struct drm_device *dev = nv_crtc->base.dev; + struct nouveau_connector *nv_connector; + struct drm_crtc *crtc = &nv_crtc->base; + struct drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_display_mode *native_mode = NULL; - struct drm_display_mode *mode = &nv_crtc->base.mode; - uint32_t outX, outY, horiz, vert; - int ret; + struct drm_display_mode *umode = &crtc->mode; + struct drm_display_mode *omode; + int scaling_mode, ret; + u32 ctrl = 0, oX, oY; NV_DEBUG_KMS(dev, "\n"); - switch (scaling_mode) { - case DRM_MODE_SCALE_NONE: - break; - default: - if (!nv_connector || !nv_connector->native_mode) { - NV_ERROR(dev, "No native mode, forcing panel scaling\n"); - scaling_mode = DRM_MODE_SCALE_NONE; + nv_connector = nouveau_crtc_connector_get(nv_crtc); + if (!nv_connector || !nv_connector->native_mode) { + NV_ERROR(dev, "no native mode, forcing panel scaling\n"); + scaling_mode = DRM_MODE_SCALE_NONE; + } else { + scaling_mode = nv_connector->scaling_mode; + } + + /* start off at the resolution we programmed the crtc for, this + * effectively handles NONE/FULL scaling + */ + if (scaling_mode != DRM_MODE_SCALE_NONE) + omode = nv_connector->native_mode; + else + omode = umode; + + oX = omode->hdisplay; + oY = omode->vdisplay; + if (omode->flags & DRM_MODE_FLAG_DBLSCAN) + oY *= 2; + + /* add overscan compensation if necessary, will keep the aspect + * ratio the same as the backend mode unless overridden by the + * user setting both hborder and vborder properties. + */ + if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || + (nv_connector->underscan == UNDERSCAN_AUTO && + nv_connector->edid && + drm_detect_hdmi_monitor(nv_connector->edid)))) { + u32 bX = nv_connector->underscan_hborder; + u32 bY = nv_connector->underscan_vborder; + u32 aspect = (oY << 19) / oX; + + if (bX) { + oX -= (bX * 2); + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; } else { - native_mode = nv_connector->native_mode; + oX -= (oX >> 4) + 32; + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; } - break; } + /* handle CENTER/ASPECT scaling, taking into account the areas + * removed already for overscan compensation + */ switch (scaling_mode) { + case DRM_MODE_SCALE_CENTER: + oX = min((u32)umode->hdisplay, oX); + oY = min((u32)umode->vdisplay, oY); + /* fall-through */ case DRM_MODE_SCALE_ASPECT: - horiz = (native_mode->hdisplay << 19) / mode->hdisplay; - vert = (native_mode->vdisplay << 19) / mode->vdisplay; - - if (vert > horiz) { - outX = (mode->hdisplay * horiz) >> 19; - outY = (mode->vdisplay * horiz) >> 19; + if (oY < oX) { + u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; + oX = ((oY * aspect) + (aspect / 2)) >> 19; } else { - outX = (mode->hdisplay * vert) >> 19; - outY = (mode->vdisplay * vert) >> 19; + u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; + oY = ((oX * aspect) + (aspect / 2)) >> 19; } break; - case DRM_MODE_SCALE_FULLSCREEN: - outX = native_mode->hdisplay; - outY = native_mode->vdisplay; - break; - case DRM_MODE_SCALE_CENTER: - case DRM_MODE_SCALE_NONE: default: - outX = mode->hdisplay; - outY = mode->vdisplay; break; } - ret = RING_SPACE(evo, update ? 7 : 5); + if (umode->hdisplay != oX || umode->vdisplay != oY || + umode->flags & DRM_MODE_FLAG_INTERLACE || + umode->flags & DRM_MODE_FLAG_DBLSCAN) + ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE; + + ret = RING_SPACE(evo, 5); if (ret) return ret; - /* Got a better name for SCALER_ACTIVE? */ - /* One day i've got to really figure out why this is needed. */ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); - if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) || - (mode->flags & DRM_MODE_FLAG_INTERLACE) || - mode->hdisplay != outX || mode->vdisplay != outY) { - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE); - } else { - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE); - } - + OUT_RING (evo, ctrl); BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2); - OUT_RING(evo, outY << 16 | outX); - OUT_RING(evo, outY << 16 | outX); + OUT_RING (evo, oY << 16 | oX); + OUT_RING (evo, oY << 16 | oX); if (update) { - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING(evo, 0); - FIRE_RING(evo); + nv50_display_flip_stop(crtc); + nv50_display_sync(dev); + nv50_display_flip_next(crtc, crtc->fb, NULL); } return 0; @@ -333,7 +365,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc) nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - kfree(nv_crtc->mode); kfree(nv_crtc); } @@ -441,39 +472,6 @@ nv50_crtc_dpms(struct drm_crtc *crtc, int mode) { } -static int -nv50_crtc_wait_complete(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; - struct nv50_display *disp = nv50_display(dev); - struct nouveau_channel *evo = disp->master; - u64 start; - int ret; - - ret = RING_SPACE(evo, 6); - if (ret) - return ret; - BEGIN_RING(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x80000000); - BEGIN_RING(evo, 0, 0x0080, 1); - OUT_RING (evo, 0); - BEGIN_RING(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x00000000); - - nv_wo32(disp->ntfy, 0x000, 0x00000000); - FIRE_RING (evo); - - start = ptimer->read(dev); - do { - if (nv_ro32(disp->ntfy, 0x000)) - return 0; - } while (ptimer->read(dev) - start < 2000000000ULL); - - return -EBUSY; -} - static void nv50_crtc_prepare(struct drm_crtc *crtc) { @@ -497,7 +495,7 @@ nv50_crtc_commit(struct drm_crtc *crtc) nv50_crtc_blank(nv_crtc, false); drm_vblank_post_modeset(dev, nv_crtc->index); - nv50_crtc_wait_complete(crtc); + nv50_display_sync(dev); nv50_display_flip_next(crtc, crtc->fb, NULL); } @@ -593,90 +591,76 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, } static int -nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, +nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, + struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nouveau_connector *nv_connector = NULL; - uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end; - uint32_t hunk1, vunk1, vunk2a, vunk2b; + u32 head = nv_crtc->index * 0x400; + u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; + u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; + u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; + u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; + u32 vblan2e = 0, vblan2s = 1; int ret; - /* Find the connector attached to this CRTC */ - nv_connector = nouveau_crtc_connector_get(nv_crtc); - - *nv_crtc->mode = *adjusted_mode; - - NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + /* hw timing description looks like this: + * + * <sync> <back porch> <---------display---------> <front porch> + * ______ + * |____________|---------------------------|____________| + * + * ^ synce ^ blanke ^ blanks ^ active + * + * interlaced modes also have 2 additional values pointing at the end + * and start of the next field's blanking period. + */ - hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start; - vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start; - hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start; - vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start; - /* I can't give this a proper name, anyone else can? */ - hunk1 = adjusted_mode->htotal - - adjusted_mode->hsync_start + adjusted_mode->hdisplay; - vunk1 = adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vdisplay; - /* Another strange value, this time only for interlaced adjusted_modes. */ - vunk2a = 2 * adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vdisplay; - vunk2b = adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vtotal; - - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { - vsync_dur /= 2; - vsync_start_to_end /= 2; - vunk1 /= 2; - vunk2a /= 2; - vunk2b /= 2; - /* magic */ - if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) { - vsync_start_to_end -= 1; - vunk1 -= 1; - vunk2a -= 1; - vunk2b -= 1; - } + hactive = mode->htotal; + hsynce = mode->hsync_end - mode->hsync_start - 1; + hbackp = mode->htotal - mode->hsync_end; + hblanke = hsynce + hbackp; + hfrontp = mode->hsync_start - mode->hdisplay; + hblanks = mode->htotal - hfrontp - 1; + + vactive = mode->vtotal * vscan / ilace; + vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; + vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; + vblanke = vsynce + vbackp; + vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; + vblanks = vactive - vfrontp - 1; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vblan2e = vactive + vsynce + vbackp; + vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); + vactive = (vactive * 2) + 1; } - ret = RING_SPACE(evo, 17); - if (ret) - return ret; - - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2); - OUT_RING(evo, adjusted_mode->clock | 0x800000); - OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0); - - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5); - OUT_RING(evo, 0); - OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal); - OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1)); - OUT_RING(evo, (vsync_start_to_end - 1) << 16 | - (hsync_start_to_end - 1)); - OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1)); - - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1); - OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1)); - } else { - OUT_RING(evo, 0); - OUT_RING(evo, 0); + ret = RING_SPACE(evo, 18); + if (ret == 0) { + BEGIN_RING(evo, 0, 0x0804 + head, 2); + OUT_RING (evo, 0x00800000 | mode->clock); + OUT_RING (evo, (ilace == 2) ? 2 : 0); + BEGIN_RING(evo, 0, 0x0810 + head, 6); + OUT_RING (evo, 0x00000000); /* border colour */ + OUT_RING (evo, (vactive << 16) | hactive); + OUT_RING (evo, ( vsynce << 16) | hsynce); + OUT_RING (evo, (vblanke << 16) | hblanke); + OUT_RING (evo, (vblanks << 16) | hblanks); + OUT_RING (evo, (vblan2e << 16) | vblan2s); + BEGIN_RING(evo, 0, 0x082c + head, 1); + OUT_RING (evo, 0x00000000); + BEGIN_RING(evo, 0, 0x0900 + head, 1); + OUT_RING (evo, 0x00000311); /* makes sync channel work */ + BEGIN_RING(evo, 0, 0x08c8 + head, 1); + OUT_RING (evo, (umode->vdisplay << 16) | umode->hdisplay); + BEGIN_RING(evo, 0, 0x08d4 + head, 1); + OUT_RING (evo, 0x00000000); /* screen position */ } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1); - OUT_RING(evo, 0); - - /* This is the actual resolution of the mode. */ - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1); - OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1); - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0)); - - nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false); - nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false); + nv_crtc->set_dither(nv_crtc, false); + nv_crtc->set_scale(nv_crtc, false); return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false); } @@ -692,7 +676,7 @@ nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, if (ret) return ret; - ret = nv50_crtc_wait_complete(crtc); + ret = nv50_display_sync(crtc->dev); if (ret) return ret; @@ -711,7 +695,7 @@ nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc, if (ret) return ret; - return nv50_crtc_wait_complete(crtc); + return nv50_display_sync(crtc->dev); } static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = { @@ -737,12 +721,6 @@ nv50_crtc_create(struct drm_device *dev, int index) if (!nv_crtc) return -ENOMEM; - nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL); - if (!nv_crtc->mode) { - kfree(nv_crtc); - return -ENOMEM; - } - /* Default CLUT parameters, will be activated on the hw upon * first mode set. */ @@ -764,7 +742,6 @@ nv50_crtc_create(struct drm_device *dev, int index) } if (ret) { - kfree(nv_crtc->mode); kfree(nv_crtc); return ret; } diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c index 808f3ec8f827..a0f2bebf49e3 100644 --- a/drivers/gpu/drm/nouveau/nv50_dac.c +++ b/drivers/gpu/drm/nouveau/nv50_dac.c @@ -200,11 +200,6 @@ nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, } static void -nv50_dac_prepare(struct drm_encoder *encoder) -{ -} - -static void nv50_dac_commit(struct drm_encoder *encoder) { } @@ -266,7 +261,7 @@ static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = { .save = nv50_dac_save, .restore = nv50_dac_restore, .mode_fixup = nv50_dac_mode_fixup, - .prepare = nv50_dac_prepare, + .prepare = nv50_dac_disconnect, .commit = nv50_dac_commit, .mode_set = nv50_dac_mode_set, .get_crtc = nv50_dac_crtc_get, diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 06de250fe617..7ba28e08ee31 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -50,9 +50,53 @@ nv50_sor_nr(struct drm_device *dev) return 4; } +static int +evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) +{ + int ret = 0; + nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000001); + nv_wr32(dev, 0x610304 + (ch * 0x08), data); + nv_wr32(dev, 0x610300 + (ch * 0x08), 0x80000001 | mthd); + if (!nv_wait(dev, 0x610300 + (ch * 0x08), 0x80000000, 0x00000000)) + ret = -EBUSY; + if (ret || (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)) + NV_INFO(dev, "EvoPIO: %d 0x%04x 0x%08x\n", ch, mthd, data); + nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000000); + return ret; +} + int nv50_display_early_init(struct drm_device *dev) { + u32 ctrl = nv_rd32(dev, 0x610200); + int i; + + /* check if master evo channel is already active, a good a sign as any + * that the display engine is in a weird state (hibernate/kexec), if + * it is, do our best to reset the display engine... + */ + if ((ctrl & 0x00000003) == 0x00000003) { + NV_INFO(dev, "PDISP: EVO(0) 0x%08x, resetting...\n", ctrl); + + /* deactivate both heads first, PDISP will disappear forever + * (well, until you power cycle) on some boards as soon as + * PMC_ENABLE is hit unless they are.. + */ + for (i = 0; i < 2; i++) { + evo_icmd(dev, 0, 0x0880 + (i * 0x400), 0x05000000); + evo_icmd(dev, 0, 0x089c + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0840 + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0844 + (i * 0x400), 0); + evo_icmd(dev, 0, 0x085c + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0874 + (i * 0x400), 0); + } + evo_icmd(dev, 0, 0x0080, 0); + + /* reset PDISP */ + nv_mask(dev, 0x000200, 0x40000000, 0x00000000); + nv_mask(dev, 0x000200, 0x40000000, 0x40000000); + } + return 0; } @@ -62,11 +106,40 @@ nv50_display_late_takedown(struct drm_device *dev) } int -nv50_display_init(struct drm_device *dev) +nv50_display_sync(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct drm_connector *connector; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + struct nv50_display *disp = nv50_display(dev); + struct nouveau_channel *evo = disp->master; + u64 start; + int ret; + + ret = RING_SPACE(evo, 6); + if (ret == 0) { + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x80000000); + BEGIN_RING(evo, 0, 0x0080, 1); + OUT_RING (evo, 0); + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x00000000); + + nv_wo32(disp->ntfy, 0x000, 0x00000000); + FIRE_RING (evo); + + start = ptimer->read(dev); + do { + if (nv_ro32(disp->ntfy, 0x000)) + return 0; + } while (ptimer->read(dev) - start < 2000000000ULL); + } + + return -EBUSY; +} + +int +nv50_display_init(struct drm_device *dev) +{ struct nouveau_channel *evo; int ret, i; u32 val; @@ -161,16 +234,6 @@ nv50_display_init(struct drm_device *dev) NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 | NV50_PDISPLAY_INTR_EN_1_CLK_UNK40); - /* enable hotplug interrupts */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct nouveau_connector *conn = nouveau_connector(connector); - - if (conn->dcb->gpio_tag == 0xff) - continue; - - pgpio->irq_enable(dev, conn->dcb->gpio_tag, true); - } - ret = nv50_evo_init(dev); if (ret) return ret; @@ -178,36 +241,19 @@ nv50_display_init(struct drm_device *dev) nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9); - ret = RING_SPACE(evo, 15); + ret = RING_SPACE(evo, 3); if (ret) return ret; BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2); - OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED); - OUT_RING(evo, NvEvoSync); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1); - OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1); - OUT_RING(evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1); - OUT_RING(evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1); - OUT_RING(evo, 0); - /* required to make display sync channels not hate life */ - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK900), 1); - OUT_RING (evo, 0x00000311); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(1, UNK900), 1); - OUT_RING (evo, 0x00000311); - FIRE_RING(evo); - if (!nv_wait(dev, 0x640004, 0xffffffff, evo->dma.put << 2)) - NV_ERROR(dev, "evo pushbuf stalled\n"); - + OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED); + OUT_RING (evo, NvEvoSync); - return 0; + return nv50_display_sync(dev); } -static int nv50_display_disable(struct drm_device *dev) +void +nv50_display_fini(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_display *disp = nv50_display(dev); struct nouveau_channel *evo = disp->master; struct drm_crtc *drm_crtc; @@ -270,18 +316,10 @@ static int nv50_display_disable(struct drm_device *dev) /* disable interrupts. */ nv_wr32(dev, NV50_PDISPLAY_INTR_EN_1, 0x00000000); - - /* disable hotplug interrupts */ - nv_wr32(dev, 0xe054, 0xffffffff); - nv_wr32(dev, 0xe050, 0x00000000); - if (dev_priv->chipset >= 0x90) { - nv_wr32(dev, 0xe074, 0xffffffff); - nv_wr32(dev, 0xe070, 0x00000000); - } - return 0; } -int nv50_display_create(struct drm_device *dev) +int +nv50_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct dcb_table *dcb = &dev_priv->vbios.dcb; @@ -341,7 +379,7 @@ int nv50_display_create(struct drm_device *dev) tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); nouveau_irq_register(dev, 26, nv50_display_isr); - ret = nv50_display_init(dev); + ret = nv50_evo_create(dev); if (ret) { nv50_display_destroy(dev); return ret; @@ -357,7 +395,7 @@ nv50_display_destroy(struct drm_device *dev) NV_DEBUG_KMS(dev, "\n"); - nv50_display_disable(dev); + nv50_evo_destroy(dev); nouveau_irq_unregister(dev, 26); kfree(disp); } @@ -521,7 +559,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb, } else { /* determine number of lvds links */ if (nv_connector && nv_connector->edid && - nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { /* http://www.spwg.org */ if (((u8 *)nv_connector->edid)[121] == 2) script |= 0x0100; @@ -722,8 +760,8 @@ nv50_display_unk20_handler(struct drm_device *dev) if (crtc >= 0) { pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)); pclk &= 0x003fffff; - - nv50_crtc_set_clock(dev, crtc, pclk); + if (pclk) + nv50_crtc_set_clock(dev, crtc, pclk); tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc)); tmp &= ~0x000000f; diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index c2da503a22aa..95874f7c043c 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -69,14 +69,18 @@ int nv50_display_early_init(struct drm_device *dev); void nv50_display_late_takedown(struct drm_device *dev); int nv50_display_create(struct drm_device *dev); int nv50_display_init(struct drm_device *dev); +void nv50_display_fini(struct drm_device *dev); void nv50_display_destroy(struct drm_device *dev); int nv50_crtc_blank(struct nouveau_crtc *, bool blank); int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); +int nv50_display_sync(struct drm_device *); int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, struct nouveau_channel *chan); void nv50_display_flip_stop(struct drm_crtc *); +int nv50_evo_create(struct drm_device *dev); +void nv50_evo_destroy(struct drm_device *dev); int nv50_evo_init(struct drm_device *dev); void nv50_evo_fini(struct drm_device *dev); void nv50_evo_dmaobj_init(struct nouveau_gpuobj *, u32 memtype, u64 base, diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c index c99d9751880c..9b962e989d7c 100644 --- a/drivers/gpu/drm/nouveau/nv50_evo.c +++ b/drivers/gpu/drm/nouveau/nv50_evo.c @@ -218,7 +218,7 @@ nv50_evo_channel_fini(struct nouveau_channel *evo) } } -static void +void nv50_evo_destroy(struct drm_device *dev) { struct nv50_display *disp = nv50_display(dev); @@ -235,7 +235,7 @@ nv50_evo_destroy(struct drm_device *dev) nv50_evo_channel_del(&disp->master); } -static int +int nv50_evo_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -388,12 +388,6 @@ nv50_evo_init(struct drm_device *dev) struct nv50_display *disp = nv50_display(dev); int ret, i; - if (!disp->master) { - ret = nv50_evo_create(dev); - if (ret) - return ret; - } - ret = nv50_evo_channel_init(disp->master); if (ret) return ret; @@ -420,6 +414,4 @@ nv50_evo_fini(struct drm_device *dev) if (disp->master) nv50_evo_channel_fini(disp->master); - - nv50_evo_destroy(dev); } diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index c34a074f7ea1..3bc2a565c20b 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -230,6 +230,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan) struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *ramfc = NULL; + uint64_t ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4; unsigned long flags; int ret; @@ -280,8 +281,9 @@ nv50_fifo_create_context(struct nouveau_channel *chan) nv_wo32(ramfc, 0x7c, 0x30000001); nv_wo32(ramfc, 0x78, 0x00000000); nv_wo32(ramfc, 0x3c, 0x403f6078); - nv_wo32(ramfc, 0x50, chan->pushbuf_base + chan->dma.ib_base * 4); - nv_wo32(ramfc, 0x54, drm_order(chan->dma.ib_max + 1) << 16); + nv_wo32(ramfc, 0x50, lower_32_bits(ib_offset)); + nv_wo32(ramfc, 0x54, upper_32_bits(ib_offset) | + drm_order(chan->dma.ib_max + 1) << 16); if (dev_priv->chipset != 0x50) { nv_wo32(chan->ramin, 0, chan->id); diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c index 793a5ccca121..f429e6a8ca7a 100644 --- a/drivers/gpu/drm/nouveau/nv50_gpio.c +++ b/drivers/gpu/drm/nouveau/nv50_gpio.c @@ -25,229 +25,95 @@ #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_hw.h" +#include "nouveau_gpio.h" #include "nv50_display.h" -static void nv50_gpio_isr(struct drm_device *dev); -static void nv50_gpio_isr_bh(struct work_struct *work); - -struct nv50_gpio_priv { - struct list_head handlers; - spinlock_t lock; -}; - -struct nv50_gpio_handler { - struct drm_device *dev; - struct list_head head; - struct work_struct work; - bool inhibit; - - struct dcb_gpio_entry *gpio; - - void (*handler)(void *data, int state); - void *data; -}; - static int -nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift) +nv50_gpio_location(int line, u32 *reg, u32 *shift) { const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; - if (gpio->line >= 32) + if (line >= 32) return -EINVAL; - *reg = nv50_gpio_reg[gpio->line >> 3]; - *shift = (gpio->line & 7) << 2; + *reg = nv50_gpio_reg[line >> 3]; + *shift = (line & 7) << 2; return 0; } int -nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +nv50_gpio_drive(struct drm_device *dev, int line, int dir, int out) { - struct dcb_gpio_entry *gpio; - uint32_t r, s, v; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; + u32 reg, shift; - if (nv50_gpio_location(gpio, &r, &s)) + if (nv50_gpio_location(line, ®, &shift)) return -EINVAL; - v = nv_rd32(dev, r) >> (s + 2); - return ((v & 1) == (gpio->state[1] & 1)); + nv_mask(dev, reg, 7 << shift, (((dir ^ 1) << 1) | out) << shift); + return 0; } int -nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +nv50_gpio_sense(struct drm_device *dev, int line) { - struct dcb_gpio_entry *gpio; - uint32_t r, s, v; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; + u32 reg, shift; - if (nv50_gpio_location(gpio, &r, &s)) + if (nv50_gpio_location(line, ®, &shift)) return -EINVAL; - v = nv_rd32(dev, r) & ~(0x3 << s); - v |= (gpio->state[state] ^ 2) << s; - nv_wr32(dev, r, v); - return 0; + return !!(nv_rd32(dev, reg) & (4 << shift)); } -int -nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +void +nv50_gpio_irq_enable(struct drm_device *dev, int line, bool on) { - struct dcb_gpio_entry *gpio; - u32 v; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; + u32 reg = line < 16 ? 0xe050 : 0xe070; + u32 mask = 0x00010001 << (line & 0xf); - v = nv_rd32(dev, 0x00d610 + (gpio->line * 4)); - v &= 0x00004000; - return (!!v == (gpio->state[1] & 1)); + nv_wr32(dev, reg + 4, mask); + nv_mask(dev, reg + 0, mask, on ? mask : 0); } int -nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +nvd0_gpio_drive(struct drm_device *dev, int line, int dir, int out) { - struct dcb_gpio_entry *gpio; - u32 v; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; - - v = gpio->state[state] ^ 2; - - nv_mask(dev, 0x00d610 + (gpio->line * 4), 0x00003000, v << 12); + u32 data = ((dir ^ 1) << 13) | (out << 12); + nv_mask(dev, 0x00d610 + (line * 4), 0x00003000, data); + nv_mask(dev, 0x00d604, 0x00000001, 0x00000001); /* update? */ return 0; } int -nv50_gpio_irq_register(struct drm_device *dev, enum dcb_gpio_tag tag, - void (*handler)(void *, int), void *data) +nvd0_gpio_sense(struct drm_device *dev, int line) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - struct nv50_gpio_handler *gpioh; - struct dcb_gpio_entry *gpio; - unsigned long flags; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; - - gpioh = kzalloc(sizeof(*gpioh), GFP_KERNEL); - if (!gpioh) - return -ENOMEM; - - INIT_WORK(&gpioh->work, nv50_gpio_isr_bh); - gpioh->dev = dev; - gpioh->gpio = gpio; - gpioh->handler = handler; - gpioh->data = data; - - spin_lock_irqsave(&priv->lock, flags); - list_add(&gpioh->head, &priv->handlers); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; + return !!(nv_rd32(dev, 0x00d610 + (line * 4)) & 0x00004000); } -void -nv50_gpio_irq_unregister(struct drm_device *dev, enum dcb_gpio_tag tag, - void (*handler)(void *, int), void *data) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - struct nv50_gpio_handler *gpioh, *tmp; - struct dcb_gpio_entry *gpio; - LIST_HEAD(tofree); - unsigned long flags; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return; - - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry_safe(gpioh, tmp, &priv->handlers, head) { - if (gpioh->gpio != gpio || - gpioh->handler != handler || - gpioh->data != data) - continue; - list_move(&gpioh->head, &tofree); - } - spin_unlock_irqrestore(&priv->lock, flags); - - list_for_each_entry_safe(gpioh, tmp, &tofree, head) { - flush_work_sync(&gpioh->work); - kfree(gpioh); - } -} - -bool -nv50_gpio_irq_enable(struct drm_device *dev, enum dcb_gpio_tag tag, bool on) -{ - struct dcb_gpio_entry *gpio; - u32 reg, mask; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return false; - - reg = gpio->line < 16 ? 0xe050 : 0xe070; - mask = 0x00010001 << (gpio->line & 0xf); - - nv_wr32(dev, reg + 4, mask); - reg = nv_mask(dev, reg + 0, mask, on ? mask : 0); - return (reg & mask) == mask; -} - -static int -nv50_gpio_create(struct drm_device *dev) +static void +nv50_gpio_isr(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + u32 intr0, intr1 = 0; + u32 hi, lo; - INIT_LIST_HEAD(&priv->handlers); - spin_lock_init(&priv->lock); - pgpio->priv = priv; - return 0; -} + intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050); + if (dev_priv->chipset >= 0x90) + intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070); -static void -nv50_gpio_destroy(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + hi = (intr0 & 0x0000ffff) | (intr1 << 16); + lo = (intr0 >> 16) | (intr1 & 0xffff0000); + nouveau_gpio_isr(dev, 0, hi | lo); - kfree(pgpio->priv); - pgpio->priv = NULL; + nv_wr32(dev, 0xe054, intr0); + if (dev_priv->chipset >= 0x90) + nv_wr32(dev, 0xe074, intr1); } int nv50_gpio_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - int ret; - - if (!pgpio->priv) { - ret = nv50_gpio_create(dev); - if (ret) - return ret; - } /* disable, and ack any pending gpio interrupts */ nv_wr32(dev, 0xe050, 0x00000000); @@ -270,64 +136,4 @@ nv50_gpio_fini(struct drm_device *dev) if (dev_priv->chipset >= 0x90) nv_wr32(dev, 0xe070, 0x00000000); nouveau_irq_unregister(dev, 21); - - nv50_gpio_destroy(dev); -} - -static void -nv50_gpio_isr_bh(struct work_struct *work) -{ - struct nv50_gpio_handler *gpioh = - container_of(work, struct nv50_gpio_handler, work); - struct drm_nouveau_private *dev_priv = gpioh->dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - unsigned long flags; - int state; - - state = pgpio->get(gpioh->dev, gpioh->gpio->tag); - if (state < 0) - return; - - gpioh->handler(gpioh->data, state); - - spin_lock_irqsave(&priv->lock, flags); - gpioh->inhibit = false; - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void -nv50_gpio_isr(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - struct nv50_gpio_handler *gpioh; - u32 intr0, intr1 = 0; - u32 hi, lo, ch; - - intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050); - if (dev_priv->chipset >= 0x90) - intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070); - - hi = (intr0 & 0x0000ffff) | (intr1 << 16); - lo = (intr0 >> 16) | (intr1 & 0xffff0000); - ch = hi | lo; - - nv_wr32(dev, 0xe054, intr0); - if (dev_priv->chipset >= 0x90) - nv_wr32(dev, 0xe074, intr1); - - spin_lock(&priv->lock); - list_for_each_entry(gpioh, &priv->handlers, head) { - if (!(ch & (1 << gpioh->gpio->line))) - continue; - - if (gpioh->inhibit) - continue; - gpioh->inhibit = true; - - schedule_work(&gpioh->work); - } - spin_unlock(&priv->lock); } diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index ac601f7c4e1a..33d5711a918d 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -616,9 +616,9 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old, } break; case 7: /* MP error */ - if (ustatus & 0x00010000) { + if (ustatus & 0x04030000) { nv50_pgraph_mp_trap(dev, i, display); - ustatus &= ~0x00010000; + ustatus &= ~0x04030000; } break; case 8: /* TPDMA error */ diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 3d5a86b98282..03937212e9d8 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -25,122 +25,745 @@ #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_bios.h" +#include "nouveau_hw.h" #include "nouveau_pm.h" +#include "nouveau_hwsq.h" -struct nv50_pm_state { - struct nouveau_pm_level *perflvl; - struct pll_lims pll; - enum pll_types type; - int N, M, P; +enum clk_src { + clk_src_crystal, + clk_src_href, + clk_src_hclk, + clk_src_hclkm3, + clk_src_hclkm3d2, + clk_src_host, + clk_src_nvclk, + clk_src_sclk, + clk_src_mclk, + clk_src_vdec, + clk_src_dom6 }; +static u32 read_clk(struct drm_device *, enum clk_src); + +static u32 +read_div(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + switch (dev_priv->chipset) { + case 0x50: /* it exists, but only has bit 31, not the dividers.. */ + case 0x84: + case 0x86: + case 0x98: + case 0xa0: + return nv_rd32(dev, 0x004700); + case 0x92: + case 0x94: + case 0x96: + return nv_rd32(dev, 0x004800); + default: + return 0x00000000; + } +} + +static u32 +read_pll_src(struct drm_device *dev, u32 base) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 coef, ref = read_clk(dev, clk_src_crystal); + u32 rsel = nv_rd32(dev, 0x00e18c); + int P, N, M, id; + + switch (dev_priv->chipset) { + case 0x50: + case 0xa0: + switch (base) { + case 0x4020: + case 0x4028: id = !!(rsel & 0x00000004); break; + case 0x4008: id = !!(rsel & 0x00000008); break; + case 0x4030: id = 0; break; + default: + NV_ERROR(dev, "ref: bad pll 0x%06x\n", base); + return 0; + } + + coef = nv_rd32(dev, 0x00e81c + (id * 0x0c)); + ref *= (coef & 0x01000000) ? 2 : 4; + P = (coef & 0x00070000) >> 16; + N = ((coef & 0x0000ff00) >> 8) + 1; + M = ((coef & 0x000000ff) >> 0) + 1; + break; + case 0x84: + case 0x86: + case 0x92: + coef = nv_rd32(dev, 0x00e81c); + P = (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + case 0x94: + case 0x96: + case 0x98: + rsel = nv_rd32(dev, 0x00c050); + switch (base) { + case 0x4020: rsel = (rsel & 0x00000003) >> 0; break; + case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break; + case 0x4028: rsel = (rsel & 0x00001800) >> 11; break; + case 0x4030: rsel = 3; break; + default: + NV_ERROR(dev, "ref: bad pll 0x%06x\n", base); + return 0; + } + + switch (rsel) { + case 0: id = 1; break; + case 1: return read_clk(dev, clk_src_crystal); + case 2: return read_clk(dev, clk_src_href); + case 3: id = 0; break; + } + + coef = nv_rd32(dev, 0x00e81c + (id * 0x28)); + P = (nv_rd32(dev, 0x00e824 + (id * 0x28)) >> 16) & 7; + P += (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + default: + BUG_ON(1); + } + + if (M) + return (ref * N / M) >> P; + return 0; +} + +static u32 +read_pll_ref(struct drm_device *dev, u32 base) +{ + u32 src, mast = nv_rd32(dev, 0x00c040); + + switch (base) { + case 0x004028: + src = !!(mast & 0x00200000); + break; + case 0x004020: + src = !!(mast & 0x00400000); + break; + case 0x004008: + src = !!(mast & 0x00010000); + break; + case 0x004030: + src = !!(mast & 0x02000000); + break; + case 0x00e810: + return read_clk(dev, clk_src_crystal); + default: + NV_ERROR(dev, "bad pll 0x%06x\n", base); + return 0; + } + + if (src) + return read_clk(dev, clk_src_href); + return read_pll_src(dev, base); +} + +static u32 +read_pll(struct drm_device *dev, u32 base) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mast = nv_rd32(dev, 0x00c040); + u32 ctrl = nv_rd32(dev, base + 0); + u32 coef = nv_rd32(dev, base + 4); + u32 ref = read_pll_ref(dev, base); + u32 clk = 0; + int N1, N2, M1, M2; + + if (base == 0x004028 && (mast & 0x00100000)) { + /* wtf, appears to only disable post-divider on nva0 */ + if (dev_priv->chipset != 0xa0) + return read_clk(dev, clk_src_dom6); + } + + N2 = (coef & 0xff000000) >> 24; + M2 = (coef & 0x00ff0000) >> 16; + N1 = (coef & 0x0000ff00) >> 8; + M1 = (coef & 0x000000ff); + if ((ctrl & 0x80000000) && M1) { + clk = ref * N1 / M1; + if ((ctrl & 0x40000100) == 0x40000000) { + if (M2) + clk = clk * N2 / M2; + else + clk = 0; + } + } + + return clk; +} + +static u32 +read_clk(struct drm_device *dev, enum clk_src src) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mast = nv_rd32(dev, 0x00c040); + u32 P = 0; + + switch (src) { + case clk_src_crystal: + return dev_priv->crystal; + case clk_src_href: + return 100000; /* PCIE reference clock */ + case clk_src_hclk: + return read_clk(dev, clk_src_href) * 27778 / 10000; + case clk_src_hclkm3: + return read_clk(dev, clk_src_hclk) * 3; + case clk_src_hclkm3d2: + return read_clk(dev, clk_src_hclk) * 3 / 2; + case clk_src_host: + switch (mast & 0x30000000) { + case 0x00000000: return read_clk(dev, clk_src_href); + case 0x10000000: break; + case 0x20000000: /* !0x50 */ + case 0x30000000: return read_clk(dev, clk_src_hclk); + } + break; + case clk_src_nvclk: + if (!(mast & 0x00100000)) + P = (nv_rd32(dev, 0x004028) & 0x00070000) >> 16; + switch (mast & 0x00000003) { + case 0x00000000: return read_clk(dev, clk_src_crystal) >> P; + case 0x00000001: return read_clk(dev, clk_src_dom6); + case 0x00000002: return read_pll(dev, 0x004020) >> P; + case 0x00000003: return read_pll(dev, 0x004028) >> P; + } + break; + case clk_src_sclk: + P = (nv_rd32(dev, 0x004020) & 0x00070000) >> 16; + switch (mast & 0x00000030) { + case 0x00000000: + if (mast & 0x00000080) + return read_clk(dev, clk_src_host) >> P; + return read_clk(dev, clk_src_crystal) >> P; + case 0x00000010: break; + case 0x00000020: return read_pll(dev, 0x004028) >> P; + case 0x00000030: return read_pll(dev, 0x004020) >> P; + } + break; + case clk_src_mclk: + P = (nv_rd32(dev, 0x004008) & 0x00070000) >> 16; + if (nv_rd32(dev, 0x004008) & 0x00000200) { + switch (mast & 0x0000c000) { + case 0x00000000: + return read_clk(dev, clk_src_crystal) >> P; + case 0x00008000: + case 0x0000c000: + return read_clk(dev, clk_src_href) >> P; + } + } else { + return read_pll(dev, 0x004008) >> P; + } + break; + case clk_src_vdec: + P = (read_div(dev) & 0x00000700) >> 8; + switch (dev_priv->chipset) { + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0xa0: + switch (mast & 0x00000c00) { + case 0x00000000: + if (dev_priv->chipset == 0xa0) /* wtf?? */ + return read_clk(dev, clk_src_nvclk) >> P; + return read_clk(dev, clk_src_crystal) >> P; + case 0x00000400: + return 0; + case 0x00000800: + if (mast & 0x01000000) + return read_pll(dev, 0x004028) >> P; + return read_pll(dev, 0x004030) >> P; + case 0x00000c00: + return read_clk(dev, clk_src_nvclk) >> P; + } + break; + case 0x98: + switch (mast & 0x00000c00) { + case 0x00000000: + return read_clk(dev, clk_src_nvclk) >> P; + case 0x00000400: + return 0; + case 0x00000800: + return read_clk(dev, clk_src_hclkm3d2) >> P; + case 0x00000c00: + return read_clk(dev, clk_src_mclk) >> P; + } + break; + } + break; + case clk_src_dom6: + switch (dev_priv->chipset) { + case 0x50: + case 0xa0: + return read_pll(dev, 0x00e810) >> 2; + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + P = (read_div(dev) & 0x00000007) >> 0; + switch (mast & 0x0c000000) { + case 0x00000000: return read_clk(dev, clk_src_href); + case 0x04000000: break; + case 0x08000000: return read_clk(dev, clk_src_hclk); + case 0x0c000000: + return read_clk(dev, clk_src_hclkm3) >> P; + } + break; + default: + break; + } + default: + break; + } + + NV_DEBUG(dev, "unknown clock source %d 0x%08x\n", src, mast); + return 0; +} + int -nv50_pm_clock_get(struct drm_device *dev, u32 id) +nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct pll_lims pll; - int P, N, M, ret; - u32 reg0, reg1; + struct drm_nouveau_private *dev_priv = dev->dev_private; + if (dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac) + return 0; - ret = get_pll_limits(dev, id, &pll); + perflvl->core = read_clk(dev, clk_src_nvclk); + perflvl->shader = read_clk(dev, clk_src_sclk); + perflvl->memory = read_clk(dev, clk_src_mclk); + if (dev_priv->chipset != 0x50) { + perflvl->vdec = read_clk(dev, clk_src_vdec); + perflvl->dom6 = read_clk(dev, clk_src_dom6); + } + + return 0; +} + +struct nv50_pm_state { + struct hwsq_ucode mclk_hwsq; + u32 mscript; + + u32 emast; + u32 nctrl; + u32 ncoef; + u32 sctrl; + u32 scoef; + + u32 amast; + u32 pdivs; +}; + +static u32 +calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll, + u32 clk, int *N1, int *M1, int *log2P) +{ + struct nouveau_pll_vals coef; + int ret; + + ret = get_pll_limits(dev, reg, pll); if (ret) - return ret; + return 0; + + pll->vco2.maxfreq = 0; + pll->refclk = read_pll_ref(dev, reg); + if (!pll->refclk) + return 0; - reg0 = nv_rd32(dev, pll.reg + 0); - reg1 = nv_rd32(dev, pll.reg + 4); + ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef); + if (ret == 0) + return 0; - if ((reg0 & 0x80000000) == 0) { - if (id == PLL_SHADER) { - NV_DEBUG(dev, "Shader PLL is disabled. " - "Shader clock is twice the core\n"); - ret = nv50_pm_clock_get(dev, PLL_CORE); - if (ret > 0) - return ret << 1; - } else if (id == PLL_MEMORY) { - NV_DEBUG(dev, "Memory PLL is disabled. " - "Memory clock is equal to the ref_clk\n"); - return pll.refclk; + *N1 = coef.N1; + *M1 = coef.M1; + *log2P = coef.log2P; + return ret; +} + +static inline u32 +calc_div(u32 src, u32 target, int *div) +{ + u32 clk0 = src, clk1 = src; + for (*div = 0; *div <= 7; (*div)++) { + if (clk0 <= target) { + clk1 = clk0 << (*div ? 1 : 0); + break; } + clk0 >>= 1; + } + + if (target - clk0 <= clk1 - target) + return clk0; + (*div)--; + return clk1; +} + +static inline u32 +clk_same(u32 a, u32 b) +{ + return ((a / 1000) == (b / 1000)); +} + +static int +calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct pll_lims pll; + u32 mast = nv_rd32(dev, 0x00c040); + u32 ctrl = nv_rd32(dev, 0x004008); + u32 coef = nv_rd32(dev, 0x00400c); + u32 orig = ctrl; + u32 crtc_mask = 0; + int N, M, P; + int ret, i; + + /* use pcie refclock if possible, otherwise use mpll */ + ctrl &= ~0x81ff0200; + if (clk_same(freq, read_clk(dev, clk_src_href))) { + ctrl |= 0x00000200 | (pll.log2p_bias << 19); + } else { + ret = calc_pll(dev, 0x4008, &pll, freq, &N, &M, &P); + if (ret == 0) + return -EINVAL; + + ctrl |= 0x80000000 | (P << 22) | (P << 16); + ctrl |= pll.log2p_bias << 19; + coef = (N << 8) | M; + } + + mast &= ~0xc0000000; /* get MCLK_2 from HREF */ + mast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */ + + /* determine active crtcs */ + for (i = 0; i < 2; i++) { + if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK))) + crtc_mask |= (1 << i); + } + + /* build the ucode which will reclock the memory for us */ + hwsq_init(hwsq); + if (crtc_mask) { + hwsq_op5f(hwsq, crtc_mask, 0x00); /* wait for scanout */ + hwsq_op5f(hwsq, crtc_mask, 0x01); /* wait for vblank */ } + if (dev_priv->chipset >= 0x92) + hwsq_wr32(hwsq, 0x611200, 0x00003300); /* disable scanout */ + hwsq_setf(hwsq, 0x10, 0); /* disable bus access */ + hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */ + + /* prepare memory controller */ + hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ + hwsq_wr32(hwsq, 0x1002d0, 0x00000001); /* force refresh */ + hwsq_wr32(hwsq, 0x100210, 0x00000000); /* stop the automatic refresh */ + hwsq_wr32(hwsq, 0x1002dc, 0x00000001); /* start self refresh mode */ - P = (reg0 & 0x00070000) >> 16; - N = (reg1 & 0x0000ff00) >> 8; - M = (reg1 & 0x000000ff); + /* reclock memory */ + hwsq_wr32(hwsq, 0xc040, mast); + hwsq_wr32(hwsq, 0x4008, orig | 0x00000200); /* bypass MPLL */ + hwsq_wr32(hwsq, 0x400c, coef); + hwsq_wr32(hwsq, 0x4008, ctrl); - return ((pll.refclk * N / M) >> P); + /* restart memory controller */ + hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ + hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */ + hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */ + hwsq_usec(hwsq, 12); /* wait for the PLL to stabilize */ + + hwsq_usec(hwsq, 48); /* may be unnecessary: causes flickering */ + hwsq_setf(hwsq, 0x10, 1); /* enable bus access */ + hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */ + if (dev_priv->chipset >= 0x92) + hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */ + hwsq_fini(hwsq); + return 0; } void * -nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u32 id, int khz) +nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct nv50_pm_state *state; - int dummy, ret; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_pm_state *info; + struct pll_lims pll; + int ret = -EINVAL; + int N, M, P1, P2; + u32 clk, out; - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) + if (dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac) + return ERR_PTR(-ENODEV); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return ERR_PTR(-ENOMEM); - state->type = id; - state->perflvl = perflvl; - ret = get_pll_limits(dev, id, &state->pll); - if (ret < 0) { - kfree(state); - return (ret == -ENOENT) ? NULL : ERR_PTR(ret); + /* core: for the moment at least, always use nvpll */ + clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); + if (clk == 0) + goto error; + + info->emast = 0x00000003; + info->nctrl = 0x80000000 | (P1 << 19) | (P1 << 16); + info->ncoef = (N << 8) | M; + + /* shader: tie to nvclk if possible, otherwise use spll. have to be + * very careful that the shader clock is at least twice the core, or + * some chipsets will be very unhappy. i expect most or all of these + * cases will be handled by tying to nvclk, but it's possible there's + * corners + */ + if (P1-- && perflvl->shader == (perflvl->core << 1)) { + info->emast |= 0x00000020; + info->sctrl = 0x00000000 | (P1 << 19) | (P1 << 16); + info->scoef = nv_rd32(dev, 0x004024); + } else { + clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1); + if (clk == 0) + goto error; + + info->emast |= 0x00000030; + info->sctrl = 0x80000000 | (P1 << 19) | (P1 << 16); + info->scoef = (N << 8) | M; + } + + /* memory: build hwsq ucode which we'll use to reclock memory */ + info->mclk_hwsq.len = 0; + if (perflvl->memory) { + clk = calc_mclk(dev, perflvl->memory, &info->mclk_hwsq); + if (clk < 0) { + ret = clk; + goto error; + } + + info->mscript = perflvl->memscript; + } + + /* vdec: avoid modifying xpll until we know exactly how the other + * clock domains work, i suspect at least some of them can also be + * tied to xpll... + */ + info->amast = nv_rd32(dev, 0x00c040); + info->pdivs = read_div(dev); + if (perflvl->vdec) { + /* see how close we can get using nvclk as a source */ + clk = calc_div(perflvl->core, perflvl->vdec, &P1); + + /* see how close we can get using xpll/hclk as a source */ + if (dev_priv->chipset != 0x98) + out = read_pll(dev, 0x004030); + else + out = read_clk(dev, clk_src_hclkm3d2); + out = calc_div(out, perflvl->vdec, &P2); + + /* select whichever gets us closest */ + info->amast &= ~0x00000c00; + info->pdivs &= ~0x00000700; + if (abs((int)perflvl->vdec - clk) <= + abs((int)perflvl->vdec - out)) { + if (dev_priv->chipset != 0x98) + info->amast |= 0x00000c00; + info->pdivs |= P1 << 8; + } else { + info->amast |= 0x00000800; + info->pdivs |= P2 << 8; + } + } + + /* dom6: nfi what this is, but we're limited to various combinations + * of the host clock frequency + */ + if (perflvl->dom6) { + info->amast &= ~0x0c000000; + if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) { + info->amast |= 0x00000000; + } else + if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) { + info->amast |= 0x08000000; + } else { + clk = read_clk(dev, clk_src_hclk) * 3; + clk = calc_div(clk, perflvl->dom6, &P1); + + info->amast |= 0x0c000000; + info->pdivs = (info->pdivs & ~0x00000007) | P1; + } } - ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, &state->M, - &dummy, &dummy, &state->P); - if (ret < 0) { - kfree(state); - return ERR_PTR(ret); + return info; +error: + kfree(info); + return ERR_PTR(ret); +} + +static int +prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 hwsq_data, hwsq_kick; + int i; + + if (dev_priv->chipset < 0x90) { + hwsq_data = 0x001400; + hwsq_kick = 0x00000003; + } else { + hwsq_data = 0x080000; + hwsq_kick = 0x00000001; } - return state; + /* upload hwsq ucode */ + nv_mask(dev, 0x001098, 0x00000008, 0x00000000); + nv_wr32(dev, 0x001304, 0x00000000); + for (i = 0; i < hwsq->len / 4; i++) + nv_wr32(dev, hwsq_data + (i * 4), hwsq->ptr.u32[i]); + nv_mask(dev, 0x001098, 0x00000018, 0x00000018); + + /* launch, and wait for completion */ + nv_wr32(dev, 0x00130c, hwsq_kick); + if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) { + NV_ERROR(dev, "hwsq ucode exec timed out\n"); + NV_ERROR(dev, "0x001308: 0x%08x\n", nv_rd32(dev, 0x001308)); + for (i = 0; i < hwsq->len / 4; i++) { + NV_ERROR(dev, "0x%06x: 0x%08x\n", 0x1400 + (i * 4), + nv_rd32(dev, 0x001400 + (i * 4))); + } + + return -EIO; + } + + return 0; } -void -nv50_pm_clock_set(struct drm_device *dev, void *pre_state) +int +nv50_pm_clocks_set(struct drm_device *dev, void *data) { - struct nv50_pm_state *state = pre_state; - struct nouveau_pm_level *perflvl = state->perflvl; - u32 reg = state->pll.reg, tmp; - struct bit_entry BIT_M; - u16 script; - int N = state->N; - int M = state->M; - int P = state->P; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_pm_state *info = data; + struct bit_entry M; + int ret = 0; - if (state->type == PLL_MEMORY && perflvl->memscript && - bit_table(dev, 'M', &BIT_M) == 0 && - BIT_M.version == 1 && BIT_M.length >= 0x0b) { - script = ROM16(BIT_M.data[0x05]); - if (script) - nouveau_bios_run_init_table(dev, script, NULL, -1); - script = ROM16(BIT_M.data[0x07]); - if (script) - nouveau_bios_run_init_table(dev, script, NULL, -1); - script = ROM16(BIT_M.data[0x09]); - if (script) - nouveau_bios_run_init_table(dev, script, NULL, -1); + /* halt and idle execution engines */ + nv_mask(dev, 0x002504, 0x00000001, 0x00000001); + if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) + goto error; - nouveau_bios_run_init_table(dev, perflvl->memscript, NULL, -1); + /* memory: it is *very* important we change this first, the ucode + * we build in pre() now has hardcoded 0xc040 values, which can't + * change before we execute it or the engine clocks may end up + * messed up. + */ + if (info->mclk_hwsq.len) { + /* execute some scripts that do ??? from the vbios.. */ + if (!bit_table(dev, 'M', &M) && M.version == 1) { + if (M.length >= 6) + nouveau_bios_init_exec(dev, ROM16(M.data[5])); + if (M.length >= 8) + nouveau_bios_init_exec(dev, ROM16(M.data[7])); + if (M.length >= 10) + nouveau_bios_init_exec(dev, ROM16(M.data[9])); + nouveau_bios_init_exec(dev, info->mscript); + } + + ret = prog_mclk(dev, &info->mclk_hwsq); + if (ret) + goto resume; } - if (state->type == PLL_MEMORY) { - nv_wr32(dev, 0x100210, 0); - nv_wr32(dev, 0x1002dc, 1); + /* reclock vdec/dom6 */ + nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000); + switch (dev_priv->chipset) { + case 0x92: + case 0x94: + case 0x96: + nv_mask(dev, 0x004800, 0x00000707, info->pdivs); + break; + default: + nv_mask(dev, 0x004700, 0x00000707, info->pdivs); + break; } + nv_mask(dev, 0x00c040, 0x0c000c00, info->amast); - tmp = nv_rd32(dev, reg + 0) & 0xfff8ffff; - tmp |= 0x80000000 | (P << 16); - nv_wr32(dev, reg + 0, tmp); - nv_wr32(dev, reg + 4, (N << 8) | M); + /* core/shader: make sure sclk/nvclk are disconnected from their + * plls (nvclk to dom6, sclk to hclk), modify the plls, and + * reconnect sclk/nvclk to their new clock source + */ + if (dev_priv->chipset < 0x92) + nv_mask(dev, 0x00c040, 0x001000b0, 0x00100080); /* grrr! */ + else + nv_mask(dev, 0x00c040, 0x000000b3, 0x00000081); + nv_mask(dev, 0x004020, 0xc03f0100, info->sctrl); + nv_wr32(dev, 0x004024, info->scoef); + nv_mask(dev, 0x004028, 0xc03f0100, info->nctrl); + nv_wr32(dev, 0x00402c, info->ncoef); + nv_mask(dev, 0x00c040, 0x00100033, info->emast); + + goto resume; +error: + ret = -EBUSY; +resume: + nv_mask(dev, 0x002504, 0x00000001, 0x00000000); + kfree(info); + return ret; +} - if (state->type == PLL_MEMORY) { - nv_wr32(dev, 0x1002dc, 0); - nv_wr32(dev, 0x100210, 0x80000000); +static int +pwm_info(struct drm_device *dev, int *line, int *ctrl, int *indx) +{ + if (*line == 0x04) { + *ctrl = 0x00e100; + *line = 4; + *indx = 0; + } else + if (*line == 0x09) { + *ctrl = 0x00e100; + *line = 9; + *indx = 1; + } else + if (*line == 0x10) { + *ctrl = 0x00e28c; + *line = 0; + *indx = 0; + } else { + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", *line); + return -ENODEV; + } + + return 0; +} + +int +nv50_pm_pwm_get(struct drm_device *dev, int line, u32 *divs, u32 *duty) +{ + int ctrl, id, ret = pwm_info(dev, &line, &ctrl, &id); + if (ret) + return ret; + + if (nv_rd32(dev, ctrl) & (1 << line)) { + *divs = nv_rd32(dev, 0x00e114 + (id * 8)); + *duty = nv_rd32(dev, 0x00e118 + (id * 8)); + return 0; } - kfree(state); + return -EINVAL; } +int +nv50_pm_pwm_set(struct drm_device *dev, int line, u32 divs, u32 duty) +{ + int ctrl, id, ret = pwm_info(dev, &line, &ctrl, &id); + if (ret) + return ret; + + nv_mask(dev, ctrl, 0x00010001 << line, 0x00000001 << line); + nv_wr32(dev, 0x00e114 + (id * 8), divs); + nv_wr32(dev, 0x00e118 + (id * 8), duty | 0x80000000); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index 2633aa8554eb..c4423ba9c9bf 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -60,6 +60,8 @@ nv50_sor_disconnect(struct drm_encoder *encoder) BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING (evo, 0); + nouveau_hdmi_mode_set(encoder, NULL); + nv_encoder->crtc = NULL; nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; } @@ -172,6 +174,12 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, static void nv50_sor_prepare(struct drm_encoder *encoder) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + nv50_sor_disconnect(encoder); + if (nv_encoder->dcb->type == OUTPUT_DP) { + /* avoid race between link training and supervisor intr */ + nv50_display_sync(encoder->dev); + } } static void @@ -180,8 +188,8 @@ nv50_sor_commit(struct drm_encoder *encoder) } static void -nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, + struct drm_display_mode *mode) { struct nouveau_channel *evo = nv50_display(encoder->dev)->master; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); @@ -193,24 +201,27 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n", nv_encoder->or, nv_encoder->dcb->type, crtc->index); + nv_encoder->crtc = encoder->crtc; switch (nv_encoder->dcb->type) { case OUTPUT_TMDS: if (nv_encoder->dcb->sorconf.link & 1) { - if (adjusted_mode->clock < 165000) + if (mode->clock < 165000) mode_ctl = 0x0100; else mode_ctl = 0x0500; } else mode_ctl = 0x0200; + + nouveau_hdmi_mode_set(encoder, mode); break; case OUTPUT_DP: nv_connector = nouveau_encoder_connector_get(nv_encoder); if (nv_connector && nv_connector->base.display_info.bpc == 6) { - nv_encoder->dp.datarate = crtc->mode->clock * 18 / 8; + nv_encoder->dp.datarate = mode->clock * 18 / 8; mode_ctl |= 0x00020000; } else { - nv_encoder->dp.datarate = crtc->mode->clock * 24 / 8; + nv_encoder->dp.datarate = mode->clock * 24 / 8; mode_ctl |= 0x00050000; } @@ -228,10 +239,10 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, else mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) + if (mode->flags & DRM_MODE_FLAG_NHSYNC) mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) + if (mode->flags & DRM_MODE_FLAG_NVSYNC) mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); @@ -239,12 +250,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, ret = RING_SPACE(evo, 2); if (ret) { NV_ERROR(dev, "no space while connecting SOR\n"); + nv_encoder->crtc = NULL; return; } BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); OUT_RING(evo, mode_ctl); - - nv_encoder->crtc = encoder->crtc; } static struct drm_crtc * diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c index 40b84f22d819..6f38ceae3aa4 100644 --- a/drivers/gpu/drm/nouveau/nv50_vm.c +++ b/drivers/gpu/drm/nouveau/nv50_vm.c @@ -48,7 +48,7 @@ nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, phys |= 0x60; else if (coverage <= 64 * 1024 * 1024) phys |= 0x40; - else if (coverage < 128 * 1024 * 1024) + else if (coverage <= 128 * 1024 * 1024) phys |= 0x20; } diff --git a/drivers/gpu/drm/nouveau/nv84_bsp.c b/drivers/gpu/drm/nouveau/nv84_bsp.c new file mode 100644 index 000000000000..74875739bcc0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv84_bsp.c @@ -0,0 +1,83 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +/*XXX: This stub is currently used on NV98+ also, as soon as this becomes + * more than just an enable/disable stub this needs to be split out to + * nv98_bsp.c... + */ + +struct nv84_bsp_engine { + struct nouveau_exec_engine base; +}; + +static int +nv84_bsp_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00008000)) + return 0; + + nv_mask(dev, 0x000200, 0x00008000, 0x00000000); + return 0; +} + +static int +nv84_bsp_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00008000, 0x00000000); + nv_mask(dev, 0x000200, 0x00008000, 0x00008000); + return 0; +} + +static void +nv84_bsp_destroy(struct drm_device *dev, int engine) +{ + struct nv84_bsp_engine *pbsp = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, BSP); + + kfree(pbsp); +} + +int +nv84_bsp_create(struct drm_device *dev) +{ + struct nv84_bsp_engine *pbsp; + + pbsp = kzalloc(sizeof(*pbsp), GFP_KERNEL); + if (!pbsp) + return -ENOMEM; + + pbsp->base.destroy = nv84_bsp_destroy; + pbsp->base.init = nv84_bsp_init; + pbsp->base.fini = nv84_bsp_fini; + + NVOBJ_ENGINE_ADD(dev, BSP, &pbsp->base); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv84_vp.c b/drivers/gpu/drm/nouveau/nv84_vp.c new file mode 100644 index 000000000000..6570d300ab85 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv84_vp.c @@ -0,0 +1,83 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +/*XXX: This stub is currently used on NV98+ also, as soon as this becomes + * more than just an enable/disable stub this needs to be split out to + * nv98_vp.c... + */ + +struct nv84_vp_engine { + struct nouveau_exec_engine base; +}; + +static int +nv84_vp_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00020000)) + return 0; + + nv_mask(dev, 0x000200, 0x00020000, 0x00000000); + return 0; +} + +static int +nv84_vp_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00020000, 0x00000000); + nv_mask(dev, 0x000200, 0x00020000, 0x00020000); + return 0; +} + +static void +nv84_vp_destroy(struct drm_device *dev, int engine) +{ + struct nv84_vp_engine *pvp = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, VP); + + kfree(pvp); +} + +int +nv84_vp_create(struct drm_device *dev) +{ + struct nv84_vp_engine *pvp; + + pvp = kzalloc(sizeof(*pvp), GFP_KERNEL); + if (!pvp) + return -ENOMEM; + + pvp->base.destroy = nv84_vp_destroy; + pvp->base.init = nv84_vp_init; + pvp->base.fini = nv84_vp_fini; + + NVOBJ_ENGINE_ADD(dev, VP, &pvp->base); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv98_crypt.c b/drivers/gpu/drm/nouveau/nv98_crypt.c new file mode 100644 index 000000000000..db94ff0a9fab --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv98_crypt.c @@ -0,0 +1,78 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +struct nv98_crypt_engine { + struct nouveau_exec_engine base; +}; + +static int +nv98_crypt_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00004000)) + return 0; + + nv_mask(dev, 0x000200, 0x00004000, 0x00000000); + return 0; +} + +static int +nv98_crypt_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00004000, 0x00000000); + nv_mask(dev, 0x000200, 0x00004000, 0x00004000); + return 0; +} + +static void +nv98_crypt_destroy(struct drm_device *dev, int engine) +{ + struct nv98_crypt_engine *pcrypt = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, CRYPT); + + kfree(pcrypt); +} + +int +nv98_crypt_create(struct drm_device *dev) +{ + struct nv98_crypt_engine *pcrypt; + + pcrypt = kzalloc(sizeof(*pcrypt), GFP_KERNEL); + if (!pcrypt) + return -ENOMEM; + + pcrypt->base.destroy = nv98_crypt_destroy; + pcrypt->base.init = nv98_crypt_init; + pcrypt->base.fini = nv98_crypt_fini; + + NVOBJ_ENGINE_ADD(dev, CRYPT, &pcrypt->base); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv98_ppp.c b/drivers/gpu/drm/nouveau/nv98_ppp.c new file mode 100644 index 000000000000..a987dd6e0036 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv98_ppp.c @@ -0,0 +1,78 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +struct nv98_ppp_engine { + struct nouveau_exec_engine base; +}; + +static int +nv98_ppp_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00000002)) + return 0; + + nv_mask(dev, 0x000200, 0x00000002, 0x00000000); + return 0; +} + +static int +nv98_ppp_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00000002, 0x00000000); + nv_mask(dev, 0x000200, 0x00000002, 0x00000002); + return 0; +} + +static void +nv98_ppp_destroy(struct drm_device *dev, int engine) +{ + struct nv98_ppp_engine *pppp = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, PPP); + + kfree(pppp); +} + +int +nv98_ppp_create(struct drm_device *dev) +{ + struct nv98_ppp_engine *pppp; + + pppp = kzalloc(sizeof(*pppp), GFP_KERNEL); + if (!pppp) + return -ENOMEM; + + pppp->base.destroy = nv98_ppp_destroy; + pppp->base.init = nv98_ppp_init; + pppp->base.fini = nv98_ppp_fini; + + NVOBJ_ENGINE_ADD(dev, PPP, &pppp->base); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc b/drivers/gpu/drm/nouveau/nva3_copy.fuc index eaf35f8321ee..abc36626fef0 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.fuc +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc @@ -31,8 +31,9 @@ */ ifdef(`NVA3', -.section nva3_pcopy_data, -.section nvc0_pcopy_data +.section #nva3_pcopy_data +, +.section #nvc0_pcopy_data ) ctx_object: .b32 0 @@ -42,7 +43,7 @@ ctx_dma_query: .b32 0 ctx_dma_src: .b32 0 ctx_dma_dst: .b32 0 ,) -.equ ctx_dma_count 3 +.equ #ctx_dma_count 3 ctx_query_address_high: .b32 0 ctx_query_address_low: .b32 0 ctx_query_counter: .b32 0 @@ -78,64 +79,65 @@ ctx_ycnt: .b32 0 dispatch_table: // mthd 0x0000, NAME .b16 0x000 1 -.b32 ctx_object ~0xffffffff +.b32 #ctx_object ~0xffffffff // mthd 0x0100, NOP .b16 0x040 1 -.b32 0x00010000 + cmd_nop ~0xffffffff +.b32 0x00010000 + #cmd_nop ~0xffffffff // mthd 0x0140, PM_TRIGGER .b16 0x050 1 -.b32 0x00010000 + cmd_pm_trigger ~0xffffffff +.b32 0x00010000 + #cmd_pm_trigger ~0xffffffff ifdef(`NVA3', ` // mthd 0x0180-0x018c, DMA_ -.b16 0x060 ctx_dma_count +.b16 0x060 #ctx_dma_count dispatch_dma: -.b32 0x00010000 + cmd_dma ~0xffffffff -.b32 0x00010000 + cmd_dma ~0xffffffff -.b32 0x00010000 + cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff ',) // mthd 0x0200-0x0218, SRC_TILE .b16 0x80 7 -.b32 ctx_src_tile_mode ~0x00000fff -.b32 ctx_src_xsize ~0x0007ffff -.b32 ctx_src_ysize ~0x00001fff -.b32 ctx_src_zsize ~0x000007ff -.b32 ctx_src_zoff ~0x00000fff -.b32 ctx_src_xoff ~0x0007ffff -.b32 ctx_src_yoff ~0x00001fff +.b32 #ctx_src_tile_mode ~0x00000fff +.b32 #ctx_src_xsize ~0x0007ffff +.b32 #ctx_src_ysize ~0x00001fff +.b32 #ctx_src_zsize ~0x000007ff +.b32 #ctx_src_zoff ~0x00000fff +.b32 #ctx_src_xoff ~0x0007ffff +.b32 #ctx_src_yoff ~0x00001fff // mthd 0x0220-0x0238, DST_TILE .b16 0x88 7 -.b32 ctx_dst_tile_mode ~0x00000fff -.b32 ctx_dst_xsize ~0x0007ffff -.b32 ctx_dst_ysize ~0x00001fff -.b32 ctx_dst_zsize ~0x000007ff -.b32 ctx_dst_zoff ~0x00000fff -.b32 ctx_dst_xoff ~0x0007ffff -.b32 ctx_dst_yoff ~0x00001fff +.b32 #ctx_dst_tile_mode ~0x00000fff +.b32 #ctx_dst_xsize ~0x0007ffff +.b32 #ctx_dst_ysize ~0x00001fff +.b32 #ctx_dst_zsize ~0x000007ff +.b32 #ctx_dst_zoff ~0x00000fff +.b32 #ctx_dst_xoff ~0x0007ffff +.b32 #ctx_dst_yoff ~0x00001fff // mthd 0x0300-0x0304, EXEC, WRCACHE_FLUSH .b16 0xc0 2 -.b32 0x00010000 + cmd_exec ~0xffffffff -.b32 0x00010000 + cmd_wrcache_flush ~0xffffffff +.b32 0x00010000 + #cmd_exec ~0xffffffff +.b32 0x00010000 + #cmd_wrcache_flush ~0xffffffff // mthd 0x030c-0x0340, various stuff .b16 0xc3 14 -.b32 ctx_src_address_high ~0x000000ff -.b32 ctx_src_address_low ~0xfffffff0 -.b32 ctx_dst_address_high ~0x000000ff -.b32 ctx_dst_address_low ~0xfffffff0 -.b32 ctx_src_pitch ~0x0007ffff -.b32 ctx_dst_pitch ~0x0007ffff -.b32 ctx_xcnt ~0x0000ffff -.b32 ctx_ycnt ~0x00001fff -.b32 ctx_format ~0x0333ffff -.b32 ctx_swz_const0 ~0xffffffff -.b32 ctx_swz_const1 ~0xffffffff -.b32 ctx_query_address_high ~0x000000ff -.b32 ctx_query_address_low ~0xffffffff -.b32 ctx_query_counter ~0xffffffff +.b32 #ctx_src_address_high ~0x000000ff +.b32 #ctx_src_address_low ~0xfffffff0 +.b32 #ctx_dst_address_high ~0x000000ff +.b32 #ctx_dst_address_low ~0xfffffff0 +.b32 #ctx_src_pitch ~0x0007ffff +.b32 #ctx_dst_pitch ~0x0007ffff +.b32 #ctx_xcnt ~0x0000ffff +.b32 #ctx_ycnt ~0x00001fff +.b32 #ctx_format ~0x0333ffff +.b32 #ctx_swz_const0 ~0xffffffff +.b32 #ctx_swz_const1 ~0xffffffff +.b32 #ctx_query_address_high ~0x000000ff +.b32 #ctx_query_address_low ~0xffffffff +.b32 #ctx_query_counter ~0xffffffff .b16 0x800 0 ifdef(`NVA3', -.section nva3_pcopy_code, -.section nvc0_pcopy_code +.section #nva3_pcopy_code +, +.section #nvc0_pcopy_code ) main: @@ -143,12 +145,12 @@ main: mov $sp $r0 // setup i0 handler and route fifo and ctxswitch to it - mov $r1 ih + mov $r1 #ih mov $iv0 $r1 mov $r1 0x400 movw $r2 0xfff3 sethi $r2 0 - iowr I[$r2 + 0x300] $r2 + iowr I[$r1 + 0x300] $r2 // enable interrupts or $r2 0xc @@ -164,19 +166,19 @@ main: bset $flags $p0 spin: sleep $p0 - bra spin + bra #spin // i0 handler ih: iord $r1 I[$r0 + 0x200] and $r2 $r1 0x00000008 - bra e ih_no_chsw - call chsw + bra e #ih_no_chsw + call #chsw ih_no_chsw: and $r2 $r1 0x00000004 - bra e ih_no_cmd - call dispatch + bra e #ih_no_cmd + call #dispatch ih_no_cmd: and $r1 $r1 0x0000000c @@ -235,9 +237,9 @@ ifdef(`NVA3', ` sethi $r4 0x60000 // swap! - bra $p1 swctx_load + bra $p1 #swctx_load xdst $r0 $r4 - bra swctx_done + bra #swctx_done swctx_load: xdld $r0 $r4 swctx_done: @@ -251,9 +253,9 @@ chsw: // if it's active, unload it and return xbit $r15 $r3 0x1e - bra e chsw_no_unload + bra e #chsw_no_unload bclr $flags $p1 - call swctx + call #swctx bclr $r3 0x1e iowr I[$r2] $r3 mov $r4 1 @@ -266,20 +268,20 @@ chsw: // is there a channel waiting to be loaded? xbit $r13 $r3 0x1e - bra e chsw_finish_load + bra e #chsw_finish_load bset $flags $p1 - call swctx + call #swctx ifdef(`NVA3', // load dma objects back into TARGET regs - mov $r5 ctx_dma - mov $r6 ctx_dma_count + mov $r5 #ctx_dma + mov $r6 #ctx_dma_count chsw_load_ctx_dma: ld b32 $r7 D[$r5 + $r6 * 4] add b32 $r8 $r6 0x180 shl b32 $r8 8 iowr I[$r8] $r7 sub b32 $r6 1 - bra nc chsw_load_ctx_dma + bra nc #chsw_load_ctx_dma ,) chsw_finish_load: @@ -297,7 +299,7 @@ dispatch: shl b32 $r2 0x10 // lookup method in the dispatch table, ILLEGAL_MTHD if not found - mov $r5 dispatch_table + mov $r5 #dispatch_table clear b32 $r6 clear b32 $r7 dispatch_loop: @@ -305,14 +307,14 @@ dispatch: ld b16 $r7 D[$r5 + 2] add b32 $r5 4 cmpu b32 $r4 $r6 - bra c dispatch_illegal_mthd + bra c #dispatch_illegal_mthd add b32 $r7 $r6 cmpu b32 $r4 $r7 - bra c dispatch_valid_mthd + bra c #dispatch_valid_mthd sub b32 $r7 $r6 shl b32 $r7 3 add b32 $r5 $r7 - bra dispatch_loop + bra #dispatch_loop // ensure no bits set in reserved fields, INVALID_BITFIELD dispatch_valid_mthd: @@ -322,20 +324,20 @@ dispatch: ld b32 $r5 D[$r4 + 4] and $r5 $r3 cmpu b32 $r5 0 - bra ne dispatch_invalid_bitfield + bra ne #dispatch_invalid_bitfield // depending on dispatch flags: execute method, or save data as state ld b16 $r5 D[$r4 + 0] ld b16 $r6 D[$r4 + 2] cmpu b32 $r6 0 - bra ne dispatch_cmd + bra ne #dispatch_cmd st b32 D[$r5] $r3 - bra dispatch_done + bra #dispatch_done dispatch_cmd: bclr $flags $p1 call $r5 - bra $p1 dispatch_error - bra dispatch_done + bra $p1 #dispatch_error + bra #dispatch_done dispatch_invalid_bitfield: or $r2 2 @@ -353,7 +355,7 @@ dispatch: iord $r2 I[$r0 + 0x200] and $r2 0x40 cmpu b32 $r2 0 - bra ne hostirq_wait + bra ne #hostirq_wait dispatch_done: mov $r2 0x1d00 @@ -409,10 +411,10 @@ ifdef(`NVA3', // $r2: hostirq state // $r3: data cmd_dma: - sub b32 $r4 dispatch_dma + sub b32 $r4 #dispatch_dma shr b32 $r4 1 bset $r3 0x1e - st b32 D[$r4 + ctx_dma] $r3 + st b32 D[$r4 + #ctx_dma] $r3 add b32 $r4 0x600 shl b32 $r4 6 iowr I[$r4] $r3 @@ -430,7 +432,7 @@ cmd_exec_set_format: st b32 D[$sp + 0x0c] $r0 // extract cpp, src_ncomp and dst_ncomp from FORMAT - ld b32 $r4 D[$r0 + ctx_format] + ld b32 $r4 D[$r0 + #ctx_format] extr $r5 $r4 16:17 add b32 $r5 1 extr $r6 $r4 20:21 @@ -448,22 +450,22 @@ cmd_exec_set_format: clear b32 $r11 bpc_loop: cmpu b8 $r10 4 - bra nc cmp_c0 + bra nc #cmp_c0 mulu $r12 $r10 $r5 add b32 $r12 $r11 bset $flags $p2 - bra bpc_next + bra #bpc_next cmp_c0: - bra ne cmp_c1 + bra ne #cmp_c1 mov $r12 0x10 add b32 $r12 $r11 - bra bpc_next + bra #bpc_next cmp_c1: cmpu b8 $r10 6 - bra nc cmp_zero + bra nc #cmp_zero mov $r12 0x14 add b32 $r12 $r11 - bra bpc_next + bra #bpc_next cmp_zero: mov $r12 0x80 bpc_next: @@ -471,22 +473,22 @@ cmd_exec_set_format: add b32 $r8 1 add b32 $r11 1 cmpu b32 $r11 $r5 - bra c bpc_loop + bra c #bpc_loop add b32 $r9 1 cmpu b32 $r9 $r7 - bra c ncomp_loop + bra c #ncomp_loop // SRC_XCNT = (xcnt * src_cpp), or 0 if no src ref in swz (hw will hang) mulu $r6 $r5 - st b32 D[$r0 + ctx_src_cpp] $r6 - ld b32 $r8 D[$r0 + ctx_xcnt] + st b32 D[$r0 + #ctx_src_cpp] $r6 + ld b32 $r8 D[$r0 + #ctx_xcnt] mulu $r6 $r8 - bra $p2 dst_xcnt + bra $p2 #dst_xcnt clear b32 $r6 dst_xcnt: mulu $r7 $r5 - st b32 D[$r0 + ctx_dst_cpp] $r7 + st b32 D[$r0 + #ctx_dst_cpp] $r7 mulu $r7 $r8 mov $r5 0x810 @@ -494,10 +496,10 @@ cmd_exec_set_format: iowr I[$r5 + 0x000] $r6 iowr I[$r5 + 0x100] $r7 add b32 $r5 0x800 - ld b32 $r6 D[$r0 + ctx_dst_cpp] + ld b32 $r6 D[$r0 + #ctx_dst_cpp] sub b32 $r6 1 shl b32 $r6 8 - ld b32 $r7 D[$r0 + ctx_src_cpp] + ld b32 $r7 D[$r0 + #ctx_src_cpp] sub b32 $r7 1 or $r6 $r7 iowr I[$r5 + 0x000] $r6 @@ -511,9 +513,9 @@ cmd_exec_set_format: ld b32 $r6 D[$sp + 0x0c] iowr I[$r5 + 0x300] $r6 add b32 $r5 0x400 - ld b32 $r6 D[$r0 + ctx_swz_const0] + ld b32 $r6 D[$r0 + #ctx_swz_const0] iowr I[$r5 + 0x000] $r6 - ld b32 $r6 D[$r0 + ctx_swz_const1] + ld b32 $r6 D[$r0 + #ctx_swz_const1] iowr I[$r5 + 0x100] $r6 add $sp 0x10 ret @@ -543,7 +545,7 @@ cmd_exec_set_format: // cmd_exec_set_surface_tiled: // translate TILE_MODE into Tp, Th, Td shift values - ld b32 $r7 D[$r5 + ctx_src_tile_mode] + ld b32 $r7 D[$r5 + #ctx_src_tile_mode] extr $r9 $r7 8:11 extr $r8 $r7 4:7 ifdef(`NVA3', @@ -553,9 +555,9 @@ ifdef(`NVA3', ) extr $r7 $r7 0:3 cmp b32 $r7 0xe - bra ne xtile64 + bra ne #xtile64 mov $r7 4 - bra xtileok + bra #xtileok xtile64: xbit $r7 $flags $p2 add b32 $r7 17 @@ -565,8 +567,8 @@ ifdef(`NVA3', // Op = (x * cpp) & ((1 << Tp) - 1) // Tx = (x * cpp) >> Tp - ld b32 $r10 D[$r5 + ctx_src_xoff] - ld b32 $r11 D[$r5 + ctx_src_cpp] + ld b32 $r10 D[$r5 + #ctx_src_xoff] + ld b32 $r11 D[$r5 + #ctx_src_cpp] mulu $r10 $r11 mov $r11 1 shl b32 $r11 $r7 @@ -576,7 +578,7 @@ ifdef(`NVA3', // Tyo = y & ((1 << Th) - 1) // Ty = y >> Th - ld b32 $r13 D[$r5 + ctx_src_yoff] + ld b32 $r13 D[$r5 + #ctx_src_yoff] mov $r14 1 shl b32 $r14 $r8 sub b32 $r14 1 @@ -598,8 +600,8 @@ ifdef(`NVA3', add b32 $r12 $r11 // nTx = ((w * cpp) + ((1 << Tp) - 1) >> Tp) - ld b32 $r15 D[$r5 + ctx_src_xsize] - ld b32 $r11 D[$r5 + ctx_src_cpp] + ld b32 $r15 D[$r5 + #ctx_src_xsize] + ld b32 $r11 D[$r5 + #ctx_src_cpp] mulu $r15 $r11 mov $r11 1 shl b32 $r11 $r7 @@ -609,7 +611,7 @@ ifdef(`NVA3', push $r15 // nTy = (h + ((1 << Th) - 1)) >> Th - ld b32 $r15 D[$r5 + ctx_src_ysize] + ld b32 $r15 D[$r5 + #ctx_src_ysize] mov $r11 1 shl b32 $r11 $r8 sub b32 $r11 1 @@ -629,7 +631,7 @@ ifdef(`NVA3', // Tz = z >> Td // Op += Tzo << Tys // Ts = Tys + Td - ld b32 $r8 D[$r5 + ctx_src_zoff] + ld b32 $r8 D[$r5 + #ctx_src_zoff] mov $r14 1 shl b32 $r14 $r9 sub b32 $r14 1 @@ -656,8 +658,8 @@ ifdef(`NVA3', // SRC_ADDRESS_LOW = (Ot + Op) & 0xffffffff // CFG_ADDRESS_HIGH |= ((Ot + Op) >> 32) << 16 - ld b32 $r7 D[$r5 + ctx_src_address_low] - ld b32 $r8 D[$r5 + ctx_src_address_high] + ld b32 $r7 D[$r5 + #ctx_src_address_low] + ld b32 $r8 D[$r5 + #ctx_src_address_high] add b32 $r10 $r12 add b32 $r7 $r10 adc b32 $r8 0 @@ -677,14 +679,14 @@ cmd_exec_set_surface_linear: xbit $r6 $flags $p2 add b32 $r6 0x202 shl b32 $r6 8 - ld b32 $r7 D[$r5 + ctx_src_address_low] + ld b32 $r7 D[$r5 + #ctx_src_address_low] iowr I[$r6 + 0x000] $r7 add b32 $r6 0x400 - ld b32 $r7 D[$r5 + ctx_src_address_high] + ld b32 $r7 D[$r5 + #ctx_src_address_high] shl b32 $r7 16 iowr I[$r6 + 0x000] $r7 add b32 $r6 0x400 - ld b32 $r7 D[$r5 + ctx_src_pitch] + ld b32 $r7 D[$r5 + #ctx_src_pitch] iowr I[$r6 + 0x000] $r7 ret @@ -697,7 +699,7 @@ cmd_exec_wait: loop: iord $r1 I[$r0] and $r1 1 - bra ne loop + bra ne #loop pop $r1 pop $r0 ret @@ -705,18 +707,18 @@ cmd_exec_wait: cmd_exec_query: // if QUERY_SHORT not set, write out { -, 0, TIME_LO, TIME_HI } xbit $r4 $r3 13 - bra ne query_counter - call cmd_exec_wait + bra ne #query_counter + call #cmd_exec_wait mov $r4 0x80c shl b32 $r4 6 - ld b32 $r5 D[$r0 + ctx_query_address_low] + ld b32 $r5 D[$r0 + #ctx_query_address_low] add b32 $r5 4 iowr I[$r4 + 0x000] $r5 iowr I[$r4 + 0x100] $r0 mov $r5 0xc iowr I[$r4 + 0x200] $r5 add b32 $r4 0x400 - ld b32 $r5 D[$r0 + ctx_query_address_high] + ld b32 $r5 D[$r0 + #ctx_query_address_high] shl b32 $r5 16 iowr I[$r4 + 0x000] $r5 add b32 $r4 0x500 @@ -741,16 +743,16 @@ cmd_exec_query: // write COUNTER query_counter: - call cmd_exec_wait + call #cmd_exec_wait mov $r4 0x80c shl b32 $r4 6 - ld b32 $r5 D[$r0 + ctx_query_address_low] + ld b32 $r5 D[$r0 + #ctx_query_address_low] iowr I[$r4 + 0x000] $r5 iowr I[$r4 + 0x100] $r0 mov $r5 0x4 iowr I[$r4 + 0x200] $r5 add b32 $r4 0x400 - ld b32 $r5 D[$r0 + ctx_query_address_high] + ld b32 $r5 D[$r0 + #ctx_query_address_high] shl b32 $r5 16 iowr I[$r4 + 0x000] $r5 add b32 $r4 0x500 @@ -759,7 +761,7 @@ cmd_exec_query: mov $r5 0x00001110 sethi $r5 0x13120000 iowr I[$r4 + 0x100] $r5 - ld b32 $r5 D[$r0 + ctx_query_counter] + ld b32 $r5 D[$r0 + #ctx_query_counter] add b32 $r4 0x500 iowr I[$r4 + 0x000] $r5 mov $r5 0x00002601 @@ -787,22 +789,22 @@ cmd_exec_query: // $r2: hostirq state // $r3: data cmd_exec: - call cmd_exec_wait + call #cmd_exec_wait // if format requested, call function to calculate it, otherwise // fill in cpp/xcnt for both surfaces as if (cpp == 1) xbit $r15 $r3 0 - bra e cmd_exec_no_format - call cmd_exec_set_format + bra e #cmd_exec_no_format + call #cmd_exec_set_format mov $r4 0x200 - bra cmd_exec_init_src_surface + bra #cmd_exec_init_src_surface cmd_exec_no_format: mov $r6 0x810 shl b32 $r6 6 mov $r7 1 - st b32 D[$r0 + ctx_src_cpp] $r7 - st b32 D[$r0 + ctx_dst_cpp] $r7 - ld b32 $r7 D[$r0 + ctx_xcnt] + st b32 D[$r0 + #ctx_src_cpp] $r7 + st b32 D[$r0 + #ctx_dst_cpp] $r7 + ld b32 $r7 D[$r0 + #ctx_xcnt] iowr I[$r6 + 0x000] $r7 iowr I[$r6 + 0x100] $r7 clear b32 $r4 @@ -811,28 +813,28 @@ cmd_exec: bclr $flags $p2 clear b32 $r5 xbit $r15 $r3 4 - bra e src_tiled - call cmd_exec_set_surface_linear - bra cmd_exec_init_dst_surface + bra e #src_tiled + call #cmd_exec_set_surface_linear + bra #cmd_exec_init_dst_surface src_tiled: - call cmd_exec_set_surface_tiled + call #cmd_exec_set_surface_tiled bset $r4 7 cmd_exec_init_dst_surface: bset $flags $p2 - mov $r5 ctx_dst_address_high - ctx_src_address_high + mov $r5 #ctx_dst_address_high - #ctx_src_address_high xbit $r15 $r3 8 - bra e dst_tiled - call cmd_exec_set_surface_linear - bra cmd_exec_kick + bra e #dst_tiled + call #cmd_exec_set_surface_linear + bra #cmd_exec_kick dst_tiled: - call cmd_exec_set_surface_tiled + call #cmd_exec_set_surface_tiled bset $r4 8 cmd_exec_kick: mov $r5 0x800 shl b32 $r5 6 - ld b32 $r6 D[$r0 + ctx_ycnt] + ld b32 $r6 D[$r0 + #ctx_ycnt] iowr I[$r5 + 0x100] $r6 mov $r6 0x0041 // SRC_TARGET = 1, DST_TARGET = 2 @@ -842,8 +844,8 @@ cmd_exec: // if requested, queue up a QUERY write after the copy has completed xbit $r15 $r3 12 - bra e cmd_exec_done - call cmd_exec_query + bra e #cmd_exec_done + call #cmd_exec_query cmd_exec_done: ret diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h index 2731de22ebe9..1f33fbdc00be 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h @@ -152,7 +152,7 @@ uint32_t nva3_pcopy_code[] = { 0xf10010fe, 0xf1040017, 0xf0fff327, - 0x22d00023, + 0x12d00023, 0x0c25f0c0, 0xf40012d0, 0x17f11031, diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 618c144b7a30..9e636e6ef6d7 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -287,12 +287,13 @@ nva3_pm_grcp_idle(void *data) return false; } -void +int nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nva3_pm_state *info = pre_state; unsigned long flags; + int ret = -EAGAIN; /* prevent any new grctx switches from starting */ spin_lock_irqsave(&dev_priv->context_switch_lock, flags); @@ -328,6 +329,8 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) nv_wr32(dev, 0x100210, 0x80000000); } + ret = 0; + cleanup: /* unfreeze PFIFO */ nv_mask(dev, 0x002504, 0x00000001, 0x00000000); @@ -339,4 +342,5 @@ cleanup: nv_mask(dev, 0x400824, 0x10000000, 0x10000000); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); kfree(info); + return ret; } diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h index 419903880e9d..a8d17458ced1 100644 --- a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h @@ -145,7 +145,7 @@ uint32_t nvc0_pcopy_code[] = { 0xf10010fe, 0xf1040017, 0xf0fff327, - 0x22d00023, + 0x12d00023, 0x0c25f0c0, 0xf40012d0, 0x17f11031, diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index ecfafd70cf0e..8ee3963f9030 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -875,14 +875,16 @@ nvc0_graph_create(struct drm_device *dev) case 0xcf: /* 4/0/0/0, 3 */ priv->magic_not_rop_nr = 0x03; break; + case 0xd9: /* 1/0/0/0, 1 */ + priv->magic_not_rop_nr = 0x01; + break; } if (!priv->magic_not_rop_nr) { NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n", priv->tp_nr[0], priv->tp_nr[1], priv->tp_nr[2], priv->tp_nr[3], priv->rop_nr); - /* use 0xc3's values... */ - priv->magic_not_rop_nr = 0x03; + priv->magic_not_rop_nr = 0x00; } NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */ diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.fuc b/drivers/gpu/drm/nouveau/nvc0_graph.fuc index 2a4b6dc8f9de..e6b228844a32 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_graph.fuc @@ -71,9 +71,9 @@ queue_put: ld b32 $r9 D[$r13 + 0x4] // PUT xor $r8 8 cmpu b32 $r8 $r9 - bra ne queue_put_next + bra ne #queue_put_next mov $r15 E_CMD_OVERFLOW - call error + call #error ret // store cmd/data on queue @@ -104,7 +104,7 @@ queue_get: ld b32 $r8 D[$r13 + 0x0] // GET ld b32 $r9 D[$r13 + 0x4] // PUT cmpu b32 $r8 $r9 - bra e queue_get_done + bra e #queue_get_done // fetch first cmd/data pair and $r9 $r8 7 shl b32 $r9 3 @@ -135,9 +135,9 @@ nv_rd32: nv_rd32_wait: iord $r12 I[$r11 + 0x000] xbit $r12 $r12 31 - bra ne nv_rd32_wait + bra ne #nv_rd32_wait mov $r10 6 // DONE_MMIO_RD - call wait_doneo + call #wait_doneo iord $r15 I[$r11 + 0x100] // MMIO_RDVAL ret @@ -157,7 +157,7 @@ nv_wr32: nv_wr32_wait: iord $r12 I[$r11 + 0x000] xbit $r12 $r12 31 - bra ne nv_wr32_wait + bra ne #nv_wr32_wait ret // (re)set watchdog timer @@ -193,7 +193,7 @@ $1: shl b32 $r8 6 iord $r8 I[$r8 + 0x000] // DONE xbit $r8 $r8 $r10 - bra $2 wait_done_$1 + bra $2 #wait_done_$1 trace_clr(T_WAIT) ret ') @@ -216,7 +216,7 @@ mmctx_size: add b32 $r9 $r8 add b32 $r14 4 cmpu b32 $r14 $r15 - bra ne nv_mmctx_size_loop + bra ne #nv_mmctx_size_loop mov b32 $r15 $r9 ret @@ -238,12 +238,12 @@ mmctx_xfer: shl b32 $r8 6 clear b32 $r9 or $r11 $r11 - bra e mmctx_base_disabled + bra e #mmctx_base_disabled iowr I[$r8 + 0x000] $r11 // MMCTX_BASE bset $r9 0 // BASE_EN mmctx_base_disabled: or $r14 $r14 - bra e mmctx_multi_disabled + bra e #mmctx_multi_disabled iowr I[$r8 + 0x200] $r14 // MMCTX_MULTI_STRIDE iowr I[$r8 + 0x300] $r15 // MMCTX_MULTI_MASK bset $r9 1 // MULTI_EN @@ -264,7 +264,7 @@ mmctx_xfer: mmctx_wait_free: iord $r14 I[$r8 + 0x000] // MMCTX_CTRL and $r14 0x1f - bra e mmctx_wait_free + bra e #mmctx_wait_free // queue up an entry ld b32 $r14 D[$r12] @@ -272,19 +272,19 @@ mmctx_xfer: iowr I[$r8 + 0x300] $r14 add b32 $r12 4 cmpu b32 $r12 $r13 - bra ne mmctx_exec_loop + bra ne #mmctx_exec_loop xbit $r11 $r10 2 - bra ne mmctx_stop + bra ne #mmctx_stop // wait for queue to empty mmctx_fini_wait: iord $r11 I[$r8 + 0x000] // MMCTX_CTRL and $r11 0x1f cmpu b32 $r11 0x10 - bra ne mmctx_fini_wait + bra ne #mmctx_fini_wait mov $r10 2 // DONE_MMCTX - call wait_donez - bra mmctx_done + call #wait_donez + bra #mmctx_done mmctx_stop: xbit $r11 $r10 0 shl b32 $r11 16 // DIR @@ -295,7 +295,7 @@ mmctx_xfer: // wait for STOP_TRIGGER to clear iord $r11 I[$r8 + 0x000] // MMCTX_CTRL xbit $r11 $r11 18 - bra ne mmctx_stop_wait + bra ne #mmctx_stop_wait mmctx_done: trace_clr(T_MMCTX) ret @@ -305,7 +305,7 @@ mmctx_xfer: strand_wait: push $r10 mov $r10 2 - call wait_donez + call #wait_donez pop $r10 ret @@ -316,7 +316,7 @@ strand_pre: sethi $r8 0x20000 mov $r9 0xc iowr I[$r8] $r9 - call strand_wait + call #strand_wait ret // unknown - call after issuing strand commands @@ -326,7 +326,7 @@ strand_post: sethi $r8 0x20000 mov $r9 0xd iowr I[$r8] $r9 - call strand_wait + call #strand_wait ret // Selects strand set?! @@ -341,11 +341,11 @@ strand_set: iowr I[$r10 + 0x000] $r12 // 0x93c = 0xf mov $r12 0xb iowr I[$r11 + 0x000] $r12 // 0x928 = 0xb - call strand_wait + call #strand_wait iowr I[$r10 + 0x000] $r14 // 0x93c = <id> mov $r12 0xa iowr I[$r11 + 0x000] $r12 // 0x928 = 0xa - call strand_wait + call #strand_wait ret // Initialise strand context data @@ -357,22 +357,22 @@ strand_set: // strand_ctx_init: trace_set(T_STRINIT) - call strand_pre + call #strand_pre mov $r14 3 - call strand_set + call #strand_set mov $r10 0x46fc sethi $r10 0x20000 add b32 $r11 $r10 0x400 iowr I[$r10 + 0x100] $r0 // STRAND_FIRST_GENE = 0 mov $r12 1 iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_FIRST_GENE - call strand_wait + call #strand_wait sub b32 $r12 $r0 1 iowr I[$r10 + 0x000] $r12 // STRAND_GENE_CNT = 0xffffffff mov $r12 2 iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_GENE_CNT - call strand_wait - call strand_post + call #strand_wait + call #strand_post // read the size of each strand, poke the context offset of // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry @@ -391,7 +391,7 @@ strand_ctx_init: add b32 $r14 $r10 add b32 $r8 4 sub b32 $r9 1 - bra ne ctx_init_strand_loop + bra ne #ctx_init_strand_loop shl b32 $r14 8 sub b32 $r15 $r14 $r15 diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h index 636fe9812f79..91d44ea662d9 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.h +++ b/drivers/gpu/drm/nouveau/nvc0_graph.h @@ -87,6 +87,7 @@ nvc0_graph_class(struct drm_device *dev) case 0xc1: return 0x9197; case 0xc8: + case 0xd9: return 0x9297; default: return 0; diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c index 96b0b93d94ca..de77842b31c0 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grctx.c +++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c @@ -1268,6 +1268,17 @@ nvc0_grctx_generate_9039(struct drm_device *dev) static void nvc0_grctx_generate_90c0(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + int i; + + for (i = 0; dev_priv->chipset == 0xd9 && i < 4; i++) { + nv_mthd(dev, 0x90c0, 0x2700 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2720 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2704 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2724 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2708 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2728 + (i * 0x40), 0x00000000); + } nv_mthd(dev, 0x90c0, 0x270c, 0x00000000); nv_mthd(dev, 0x90c0, 0x272c, 0x00000000); nv_mthd(dev, 0x90c0, 0x274c, 0x00000000); @@ -1276,6 +1287,12 @@ nvc0_grctx_generate_90c0(struct drm_device *dev) nv_mthd(dev, 0x90c0, 0x27ac, 0x00000000); nv_mthd(dev, 0x90c0, 0x27cc, 0x00000000); nv_mthd(dev, 0x90c0, 0x27ec, 0x00000000); + for (i = 0; dev_priv->chipset == 0xd9 && i < 4; i++) { + nv_mthd(dev, 0x90c0, 0x2710 + (i * 0x40), 0x00014000); + nv_mthd(dev, 0x90c0, 0x2730 + (i * 0x40), 0x00014000); + nv_mthd(dev, 0x90c0, 0x2714 + (i * 0x40), 0x00000040); + nv_mthd(dev, 0x90c0, 0x2734 + (i * 0x40), 0x00000040); + } nv_mthd(dev, 0x90c0, 0x030c, 0x00000001); nv_mthd(dev, 0x90c0, 0x1944, 0x00000000); nv_mthd(dev, 0x90c0, 0x0758, 0x00000100); @@ -1471,14 +1488,20 @@ nvc0_grctx_generate_shaders(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - if (dev_priv->chipset != 0xc1) { - nv_wr32(dev, 0x405800, 0x078000bf); - nv_wr32(dev, 0x405830, 0x02180000); - } else { + if (dev_priv->chipset == 0xd9) { nv_wr32(dev, 0x405800, 0x0f8000bf); nv_wr32(dev, 0x405830, 0x02180218); + nv_wr32(dev, 0x405834, 0x08000000); + } else + if (dev_priv->chipset == 0xc1) { + nv_wr32(dev, 0x405800, 0x0f8000bf); + nv_wr32(dev, 0x405830, 0x02180218); + nv_wr32(dev, 0x405834, 0x00000000); + } else { + nv_wr32(dev, 0x405800, 0x078000bf); + nv_wr32(dev, 0x405830, 0x02180000); + nv_wr32(dev, 0x405834, 0x00000000); } - nv_wr32(dev, 0x405834, 0x00000000); nv_wr32(dev, 0x405838, 0x00000000); nv_wr32(dev, 0x405854, 0x00000000); nv_wr32(dev, 0x405870, 0x00000001); @@ -1509,7 +1532,10 @@ nvc0_grctx_generate_unk64xx(struct drm_device *dev) nv_wr32(dev, 0x4064ac, 0x00003fff); nv_wr32(dev, 0x4064b4, 0x00000000); nv_wr32(dev, 0x4064b8, 0x00000000); - if (dev_priv->chipset == 0xc1) { + if (dev_priv->chipset == 0xd9) + nv_wr32(dev, 0x4064bc, 0x00000000); + if (dev_priv->chipset == 0xc1 || + dev_priv->chipset == 0xd9) { nv_wr32(dev, 0x4064c0, 0x80140078); nv_wr32(dev, 0x4064c4, 0x0086ffff); } @@ -1550,10 +1576,23 @@ nvc0_grctx_generate_rop(struct drm_device *dev) /* ROPC_BROADCAST */ nv_wr32(dev, 0x408800, 0x02802a3c); nv_wr32(dev, 0x408804, 0x00000040); - nv_wr32(dev, 0x408808, chipset != 0xc1 ? 0x0003e00d : 0x1003e005); - nv_wr32(dev, 0x408900, 0x3080b801); - nv_wr32(dev, 0x408904, chipset != 0xc1 ? 0x02000001 : 0x62000001); - nv_wr32(dev, 0x408908, 0x00c80929); + if (chipset == 0xd9) { + nv_wr32(dev, 0x408808, 0x1043e005); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, 0x1043e005); + nv_wr32(dev, 0x408908, 0x00c8102f); + } else + if (chipset == 0xc1) { + nv_wr32(dev, 0x408808, 0x1003e005); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, 0x62000001); + nv_wr32(dev, 0x408908, 0x00c80929); + } else { + nv_wr32(dev, 0x408808, 0x0003e00d); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, 0x02000001); + nv_wr32(dev, 0x408908, 0x00c80929); + } nv_wr32(dev, 0x40890c, 0x00000000); nv_wr32(dev, 0x408980, 0x0000011d); } @@ -1572,7 +1611,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418408, 0x00000000); nv_wr32(dev, 0x41840c, 0x00001008); nv_wr32(dev, 0x418410, 0x0fff0fff); - nv_wr32(dev, 0x418414, 0x00200fff); + nv_wr32(dev, 0x418414, chipset != 0xd9 ? 0x00200fff : 0x02200fff); nv_wr32(dev, 0x418450, 0x00000000); nv_wr32(dev, 0x418454, 0x00000000); nv_wr32(dev, 0x418458, 0x00000000); @@ -1587,14 +1626,17 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418700, 0x00000002); nv_wr32(dev, 0x418704, 0x00000080); nv_wr32(dev, 0x418708, 0x00000000); - nv_wr32(dev, 0x41870c, 0x07c80000); + nv_wr32(dev, 0x41870c, chipset != 0xd9 ? 0x07c80000 : 0x00000000); nv_wr32(dev, 0x418710, 0x00000000); - nv_wr32(dev, 0x418800, 0x0006860a); + nv_wr32(dev, 0x418800, chipset != 0xd9 ? 0x0006860a : 0x7006860a); nv_wr32(dev, 0x418808, 0x00000000); nv_wr32(dev, 0x41880c, 0x00000000); nv_wr32(dev, 0x418810, 0x00000000); nv_wr32(dev, 0x418828, 0x00008442); - nv_wr32(dev, 0x418830, chipset != 0xc1 ? 0x00000001 : 0x10000001); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x418830, 0x10000001); + else + nv_wr32(dev, 0x418830, 0x00000001); nv_wr32(dev, 0x4188d8, 0x00000008); nv_wr32(dev, 0x4188e0, 0x01000000); nv_wr32(dev, 0x4188e8, 0x00000000); @@ -1602,7 +1644,12 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x4188f0, 0x00000000); nv_wr32(dev, 0x4188f4, 0x00000000); nv_wr32(dev, 0x4188f8, 0x00000000); - nv_wr32(dev, 0x4188fc, chipset != 0xc1 ? 0x00100000 : 0x00100018); + if (chipset == 0xd9) + nv_wr32(dev, 0x4188fc, 0x20100008); + else if (chipset == 0xc1) + nv_wr32(dev, 0x4188fc, 0x00100018); + else + nv_wr32(dev, 0x4188fc, 0x00100000); nv_wr32(dev, 0x41891c, 0x00ff00ff); nv_wr32(dev, 0x418924, 0x00000000); nv_wr32(dev, 0x418928, 0x00ffff00); @@ -1616,7 +1663,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418a14 + (i * 0x20), 0x00000000); nv_wr32(dev, 0x418a18 + (i * 0x20), 0x00000000); } - nv_wr32(dev, 0x418b00, 0x00000000); + nv_wr32(dev, 0x418b00, chipset != 0xd9 ? 0x00000000 : 0x00000006); nv_wr32(dev, 0x418b08, 0x0a418820); nv_wr32(dev, 0x418b0c, 0x062080e6); nv_wr32(dev, 0x418b10, 0x020398a4); @@ -1633,7 +1680,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418c24, 0x00000000); nv_wr32(dev, 0x418c28, 0x00000000); nv_wr32(dev, 0x418c2c, 0x00000000); - if (chipset == 0xc1) + if (chipset == 0xc1 || chipset == 0xd9) nv_wr32(dev, 0x418c6c, 0x00000001); nv_wr32(dev, 0x418c80, 0x20200004); nv_wr32(dev, 0x418c8c, 0x00000001); @@ -1653,7 +1700,10 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419818, 0x00000000); nv_wr32(dev, 0x41983c, 0x00038bc7); nv_wr32(dev, 0x419848, 0x00000000); - nv_wr32(dev, 0x419864, chipset != 0xc1 ? 0x0000012a : 0x00000129); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x419864, 0x00000129); + else + nv_wr32(dev, 0x419864, 0x0000012a); nv_wr32(dev, 0x419888, 0x00000000); nv_wr32(dev, 0x419a00, 0x000001f0); nv_wr32(dev, 0x419a04, 0x00000001); @@ -1663,7 +1713,9 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419a14, 0x00000200); nv_wr32(dev, 0x419a1c, 0x00000000); nv_wr32(dev, 0x419a20, 0x00000800); - if (chipset != 0xc0 && chipset != 0xc8) + if (chipset == 0xd9) + nv_wr32(dev, 0x00419ac4, 0x0017f440); + else if (chipset != 0xc0 && chipset != 0xc8) nv_wr32(dev, 0x00419ac4, 0x0007f440); nv_wr32(dev, 0x419b00, 0x0a418820); nv_wr32(dev, 0x419b04, 0x062080e6); @@ -1672,21 +1724,33 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419b10, 0x0a418820); nv_wr32(dev, 0x419b14, 0x000000e6); nv_wr32(dev, 0x419bd0, 0x00900103); - nv_wr32(dev, 0x419be0, chipset != 0xc1 ? 0x00000001 : 0x00400001); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x419be0, 0x00400001); + else + nv_wr32(dev, 0x419be0, 0x00000001); nv_wr32(dev, 0x419be4, 0x00000000); - nv_wr32(dev, 0x419c00, 0x00000002); + nv_wr32(dev, 0x419c00, chipset != 0xd9 ? 0x00000002 : 0x0000000a); nv_wr32(dev, 0x419c04, 0x00000006); nv_wr32(dev, 0x419c08, 0x00000002); nv_wr32(dev, 0x419c20, 0x00000000); - if (chipset == 0xce || chipset == 0xcf) + if (dev_priv->chipset == 0xd9) { + nv_wr32(dev, 0x419c24, 0x00084210); + nv_wr32(dev, 0x419c28, 0x3cf3cf3c); nv_wr32(dev, 0x419cb0, 0x00020048); - else + } else + if (chipset == 0xce || chipset == 0xcf) { + nv_wr32(dev, 0x419cb0, 0x00020048); + } else { nv_wr32(dev, 0x419cb0, 0x00060048); + } nv_wr32(dev, 0x419ce8, 0x00000000); nv_wr32(dev, 0x419cf4, 0x00000183); - nv_wr32(dev, 0x419d20, chipset != 0xc1 ? 0x02180000 : 0x12180000); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x419d20, 0x12180000); + else + nv_wr32(dev, 0x419d20, 0x02180000); nv_wr32(dev, 0x419d24, 0x00001fff); - if (chipset == 0xc1) + if (chipset == 0xc1 || chipset == 0xd9) nv_wr32(dev, 0x419d44, 0x02180218); nv_wr32(dev, 0x419e04, 0x00000000); nv_wr32(dev, 0x419e08, 0x00000000); @@ -1986,6 +2050,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x00000215, 0x00000040); nv_icmd(dev, 0x00000216, 0x00000040); nv_icmd(dev, 0x00000217, 0x00000040); + if (dev_priv->chipset == 0xd9) { + for (i = 0x0400; i <= 0x0417; i++) + nv_icmd(dev, i, 0x00000040); + } nv_icmd(dev, 0x00000218, 0x0000c080); nv_icmd(dev, 0x00000219, 0x0000c080); nv_icmd(dev, 0x0000021a, 0x0000c080); @@ -1994,6 +2062,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x0000021d, 0x0000c080); nv_icmd(dev, 0x0000021e, 0x0000c080); nv_icmd(dev, 0x0000021f, 0x0000c080); + if (dev_priv->chipset == 0xd9) { + for (i = 0x0440; i <= 0x0457; i++) + nv_icmd(dev, i, 0x0000c080); + } nv_icmd(dev, 0x000000ad, 0x0000013e); nv_icmd(dev, 0x000000e1, 0x00000010); nv_icmd(dev, 0x00000290, 0x00000000); @@ -2556,7 +2628,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x0000053f, 0xffff0000); nv_icmd(dev, 0x00000585, 0x0000003f); nv_icmd(dev, 0x00000576, 0x00000003); - if (dev_priv->chipset == 0xc1) + if (dev_priv->chipset == 0xc1 || + dev_priv->chipset == 0xd9) nv_icmd(dev, 0x0000057b, 0x00000059); nv_icmd(dev, 0x00000586, 0x00000040); nv_icmd(dev, 0x00000582, 0x00000080); @@ -2658,6 +2731,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x00000957, 0x00000003); nv_icmd(dev, 0x0000095e, 0x20164010); nv_icmd(dev, 0x0000095f, 0x00000020); + if (dev_priv->chipset == 0xd9) + nv_icmd(dev, 0x0000097d, 0x00000020); nv_icmd(dev, 0x00000683, 0x00000006); nv_icmd(dev, 0x00000685, 0x003fffff); nv_icmd(dev, 0x00000687, 0x00000c48); diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc index 06f5e26d1e0f..15272be33b66 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc @@ -32,7 +32,7 @@ * - watchdog timer around ctx operations */ -.section nvc0_grgpc_data +.section #nvc0_grgpc_data include(`nvc0_graph.fuc') gpc_id: .b32 0 gpc_mmio_list_head: .b32 0 @@ -48,40 +48,45 @@ cmd_queue: queue_init // chipset descriptions chipsets: .b8 0xc0 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc0_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc0_tpc_mmio_tail .b8 0xc1 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc1_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc1_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc1_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc1_tpc_mmio_tail .b8 0xc3 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc3_tpc_mmio_tail .b8 0xc4 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc3_tpc_mmio_tail .b8 0xc8 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc0_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc0_tpc_mmio_tail .b8 0xce 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc3_tpc_mmio_tail .b8 0xcf 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvcf_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvcf_tpc_mmio_tail +.b8 0xd9 0 0 0 +.b16 #nvd9_gpc_mmio_head +.b16 #nvd9_gpc_mmio_tail +.b16 #nvd9_tpc_mmio_head +.b16 #nvd9_tpc_mmio_tail .b8 0 0 0 0 // GPC mmio lists @@ -114,6 +119,35 @@ nvc0_gpc_mmio_tail: mmctx_data(0x000c6c, 1); nvc1_gpc_mmio_tail: +nvd9_gpc_mmio_head: +mmctx_data(0x000380, 1) +mmctx_data(0x000400, 2) +mmctx_data(0x00040c, 3) +mmctx_data(0x000450, 9) +mmctx_data(0x000600, 1) +mmctx_data(0x000684, 1) +mmctx_data(0x000700, 5) +mmctx_data(0x000800, 1) +mmctx_data(0x000808, 3) +mmctx_data(0x000828, 1) +mmctx_data(0x000830, 1) +mmctx_data(0x0008d8, 1) +mmctx_data(0x0008e0, 1) +mmctx_data(0x0008e8, 6) +mmctx_data(0x00091c, 1) +mmctx_data(0x000924, 3) +mmctx_data(0x000b00, 1) +mmctx_data(0x000b08, 6) +mmctx_data(0x000bb8, 1) +mmctx_data(0x000c08, 1) +mmctx_data(0x000c10, 8) +mmctx_data(0x000c6c, 1) +mmctx_data(0x000c80, 1) +mmctx_data(0x000c8c, 1) +mmctx_data(0x001000, 3) +mmctx_data(0x001014, 1) +nvd9_gpc_mmio_tail: + // TPC mmio lists nvc0_tpc_mmio_head: mmctx_data(0x000018, 1) @@ -146,9 +180,34 @@ nvc3_tpc_mmio_tail: mmctx_data(0x000544, 1) nvc1_tpc_mmio_tail: +nvd9_tpc_mmio_head: +mmctx_data(0x000018, 1) +mmctx_data(0x00003c, 1) +mmctx_data(0x000048, 1) +mmctx_data(0x000064, 1) +mmctx_data(0x000088, 1) +mmctx_data(0x000200, 6) +mmctx_data(0x00021c, 2) +mmctx_data(0x0002c4, 1) +mmctx_data(0x000300, 6) +mmctx_data(0x0003d0, 1) +mmctx_data(0x0003e0, 2) +mmctx_data(0x000400, 3) +mmctx_data(0x000420, 3) +mmctx_data(0x0004b0, 1) +mmctx_data(0x0004e8, 1) +mmctx_data(0x0004f4, 1) +mmctx_data(0x000520, 2) +mmctx_data(0x000544, 1) +mmctx_data(0x000604, 4) +mmctx_data(0x000644, 20) +mmctx_data(0x000698, 1) +mmctx_data(0x0006e0, 1) +mmctx_data(0x000750, 3) +nvd9_tpc_mmio_tail: -.section nvc0_grgpc_code -bra init +.section #nvc0_grgpc_code +bra #init define(`include_code') include(`nvc0_graph.fuc') @@ -160,10 +219,10 @@ error: push $r14 mov $r14 -0x67ec // 0x9814 sethi $r14 0x400000 - call nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code + call #nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code add b32 $r14 0x41c mov $r15 1 - call nv_wr32 // HUB_CTXCTL_INTR_UP_SET + call #nv_wr32 // HUB_CTXCTL_INTR_UP_SET pop $r14 ret @@ -190,7 +249,7 @@ init: iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE // setup i0 handler, and route all interrupts to it - mov $r1 ih + mov $r1 #ih mov $iv0 $r1 mov $r1 0x400 iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH @@ -210,24 +269,24 @@ init: and $r2 0x1f shl b32 $r3 $r2 sub b32 $r3 1 - st b32 D[$r0 + tpc_count] $r2 - st b32 D[$r0 + tpc_mask] $r3 + st b32 D[$r0 + #tpc_count] $r2 + st b32 D[$r0 + #tpc_mask] $r3 add b32 $r1 0x400 iord $r2 I[$r1 + 0x000] // MYINDEX - st b32 D[$r0 + gpc_id] $r2 + st b32 D[$r0 + #gpc_id] $r2 // find context data for this chipset mov $r2 0x800 shl b32 $r2 6 iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r1 chipsets - 12 + mov $r1 #chipsets - 12 init_find_chipset: add b32 $r1 12 ld b32 $r3 D[$r1 + 0x00] cmpu b32 $r3 $r2 - bra e init_context + bra e #init_context cmpu b32 $r3 0 - bra ne init_find_chipset + bra ne #init_find_chipset // unknown chipset ret @@ -253,19 +312,19 @@ init: clear b32 $r15 ld b16 $r14 D[$r1 + 4] ld b16 $r15 D[$r1 + 6] - st b16 D[$r0 + gpc_mmio_list_head] $r14 - st b16 D[$r0 + gpc_mmio_list_tail] $r15 - call mmctx_size + st b16 D[$r0 + #gpc_mmio_list_head] $r14 + st b16 D[$r0 + #gpc_mmio_list_tail] $r15 + call #mmctx_size add b32 $r2 $r15 add b32 $r3 $r15 // calculate per-TPC mmio context size, store the list pointers ld b16 $r14 D[$r1 + 8] ld b16 $r15 D[$r1 + 10] - st b16 D[$r0 + tpc_mmio_list_head] $r14 - st b16 D[$r0 + tpc_mmio_list_tail] $r15 - call mmctx_size - ld b32 $r14 D[$r0 + tpc_count] + st b16 D[$r0 + #tpc_mmio_list_head] $r14 + st b16 D[$r0 + #tpc_mmio_list_tail] $r15 + call #mmctx_size + ld b32 $r14 D[$r0 + #tpc_count] mulu $r14 $r15 add b32 $r2 $r14 add b32 $r3 $r14 @@ -283,7 +342,7 @@ init: // calculate size of strand context data mov b32 $r15 $r2 - call strand_ctx_init + call #strand_ctx_init add b32 $r3 $r15 // save context size, and tell HUB we're done @@ -301,13 +360,13 @@ init: main: bset $flags $p0 sleep $p0 - mov $r13 cmd_queue - call queue_get - bra $p1 main + mov $r13 #cmd_queue + call #queue_get + bra $p1 #main // 0x0000-0x0003 are all context transfers cmpu b32 $r14 0x04 - bra nc main_not_ctx_xfer + bra nc #main_not_ctx_xfer // fetch $flags and mask off $p1/$p2 mov $r1 $flags mov $r2 0x0006 @@ -318,14 +377,14 @@ main: or $r1 $r14 mov $flags $r1 // transfer context data - call ctx_xfer - bra main + call #ctx_xfer + bra #main main_not_ctx_xfer: shl b32 $r15 $r14 16 or $r15 E_BAD_COMMAND - call error - bra main + call #error + bra #main // interrupt handler ih: @@ -342,13 +401,13 @@ ih: // incoming fifo command? iord $r10 I[$r0 + 0x200] // INTR and $r11 $r10 0x00000004 - bra e ih_no_fifo + bra e #ih_no_fifo // queue incoming fifo command for later processing mov $r11 0x1900 - mov $r13 cmd_queue + mov $r13 #cmd_queue iord $r14 I[$r11 + 0x100] // FIFO_CMD iord $r15 I[$r11 + 0x000] // FIFO_DATA - call queue_put + call #queue_put add b32 $r11 0x400 mov $r14 1 iowr I[$r11 + 0x000] $r14 // FIFO_ACK @@ -374,11 +433,11 @@ ih: // hub_barrier_done: mov $r15 1 - ld b32 $r14 D[$r0 + gpc_id] + ld b32 $r14 D[$r0 + #gpc_id] shl b32 $r15 $r14 mov $r14 -0x6be8 // 0x409418 - HUB_BAR_SET sethi $r14 0x400000 - call nv_wr32 + call #nv_wr32 ret // Disables various things, waits a bit, and re-enables them.. @@ -395,7 +454,7 @@ ctx_redswitch: mov $r15 8 ctx_redswitch_delay: sub b32 $r15 1 - bra ne ctx_redswitch_delay + bra ne #ctx_redswitch_delay mov $r15 0xa20 iowr I[$r14] $r15 // GPC_RED_SWITCH = UNK11, ENABLE, POWER ret @@ -413,8 +472,8 @@ ctx_xfer: mov $r1 0xa04 shl b32 $r1 6 iowr I[$r1 + 0x000] $r15// MEM_BASE - bra not $p1 ctx_xfer_not_load - call ctx_redswitch + bra not $p1 #ctx_xfer_not_load + call #ctx_redswitch ctx_xfer_not_load: // strands @@ -422,7 +481,7 @@ ctx_xfer: sethi $r1 0x20000 mov $r2 0xc iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call strand_wait + call #strand_wait mov $r2 0x47fc sethi $r2 0x20000 iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 @@ -435,46 +494,46 @@ ctx_xfer: or $r10 2 // first mov $r11 0x0000 sethi $r11 0x500000 - ld b32 $r12 D[$r0 + gpc_id] + ld b32 $r12 D[$r0 + #gpc_id] shl b32 $r12 15 add b32 $r11 $r12 // base = NV_PGRAPH_GPCn - ld b32 $r12 D[$r0 + gpc_mmio_list_head] - ld b32 $r13 D[$r0 + gpc_mmio_list_tail] + ld b32 $r12 D[$r0 + #gpc_mmio_list_head] + ld b32 $r13 D[$r0 + #gpc_mmio_list_tail] mov $r14 0 // not multi - call mmctx_xfer + call #mmctx_xfer // per-TPC mmio context xbit $r10 $flags $p1 // direction or $r10 4 // last mov $r11 0x4000 sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0 - ld b32 $r12 D[$r0 + gpc_id] + ld b32 $r12 D[$r0 + #gpc_id] shl b32 $r12 15 add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0 - ld b32 $r12 D[$r0 + tpc_mmio_list_head] - ld b32 $r13 D[$r0 + tpc_mmio_list_tail] - ld b32 $r15 D[$r0 + tpc_mask] + ld b32 $r12 D[$r0 + #tpc_mmio_list_head] + ld b32 $r13 D[$r0 + #tpc_mmio_list_tail] + ld b32 $r15 D[$r0 + #tpc_mask] mov $r14 0x800 // stride = 0x800 - call mmctx_xfer + call #mmctx_xfer // wait for strands to finish - call strand_wait + call #strand_wait // if load, or a save without a load following, do some // unknown stuff that's done after finishing a block of // strand commands - bra $p1 ctx_xfer_post - bra not $p2 ctx_xfer_done + bra $p1 #ctx_xfer_post + bra not $p2 #ctx_xfer_done ctx_xfer_post: mov $r1 0x4afc sethi $r1 0x20000 mov $r2 0xd iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0d - call strand_wait + call #strand_wait // mark completion in HUB's barrier ctx_xfer_done: - call hub_barrier_done + call #hub_barrier_done ret .align 256 diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h index 6f820324480e..a988b8ad00ac 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h @@ -25,26 +25,29 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, 0x00000000, 0x000000c0, - 0x011c00bc, - 0x01700120, + 0x012800c8, + 0x01e40194, 0x000000c1, - 0x012000bc, - 0x01840120, + 0x012c00c8, + 0x01f80194, 0x000000c3, - 0x011c00bc, - 0x01800120, + 0x012800c8, + 0x01f40194, 0x000000c4, - 0x011c00bc, - 0x01800120, + 0x012800c8, + 0x01f40194, 0x000000c8, - 0x011c00bc, - 0x01700120, + 0x012800c8, + 0x01e40194, 0x000000ce, - 0x011c00bc, - 0x01800120, + 0x012800c8, + 0x01f40194, 0x000000cf, - 0x011c00bc, - 0x017c0120, + 0x012800c8, + 0x01f00194, + 0x000000d9, + 0x0194012c, + 0x025401f8, 0x00000000, 0x00000380, 0x14000400, @@ -71,6 +74,32 @@ uint32_t nvc0_grgpc_data[] = { 0x08001000, 0x00001014, 0x00000c6c, + 0x00000380, + 0x04000400, + 0x0800040c, + 0x20000450, + 0x00000600, + 0x00000684, + 0x10000700, + 0x00000800, + 0x08000808, + 0x00000828, + 0x00000830, + 0x000008d8, + 0x000008e0, + 0x140008e8, + 0x0000091c, + 0x08000924, + 0x00000b00, + 0x14000b08, + 0x00000bb8, + 0x00000c08, + 0x1c000c10, + 0x00000c6c, + 0x00000c80, + 0x00000c8c, + 0x08001000, + 0x00001014, 0x00000018, 0x0000003c, 0x00000048, @@ -96,6 +125,29 @@ uint32_t nvc0_grgpc_data[] = { 0x000006e0, 0x000004bc, 0x00000544, + 0x00000018, + 0x0000003c, + 0x00000048, + 0x00000064, + 0x00000088, + 0x14000200, + 0x0400021c, + 0x000002c4, + 0x14000300, + 0x000003d0, + 0x040003e0, + 0x08000400, + 0x08000420, + 0x000004b0, + 0x000004e8, + 0x000004f4, + 0x04000520, + 0x00000544, + 0x0c000604, + 0x4c000644, + 0x00000698, + 0x000006e0, + 0x08000750, }; uint32_t nvc0_grgpc_code[] = { diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc index e4f8c7e89ddd..98acddb2c5bb 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc @@ -27,7 +27,7 @@ * m4 nvc0_grhub.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_grhub.fuc.h */ -.section nvc0_grhub_data +.section #nvc0_grhub_data include(`nvc0_graph.fuc') gpc_count: .b32 0 rop_count: .b32 0 @@ -39,26 +39,29 @@ ctx_current: .b32 0 chipsets: .b8 0xc0 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc1 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc1_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc1_hub_mmio_tail .b8 0xc3 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc4 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc8 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xce 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xcf 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail +.b8 0xd9 0 0 0 +.b16 #nvd9_hub_mmio_head +.b16 #nvd9_hub_mmio_tail .b8 0 0 0 0 nvc0_hub_mmio_head: @@ -105,6 +108,48 @@ nvc0_hub_mmio_tail: mmctx_data(0x4064c0, 2) nvc1_hub_mmio_tail: +nvd9_hub_mmio_head: +mmctx_data(0x17e91c, 2) +mmctx_data(0x400204, 2) +mmctx_data(0x404004, 10) +mmctx_data(0x404044, 1) +mmctx_data(0x404094, 14) +mmctx_data(0x4040d0, 7) +mmctx_data(0x4040f8, 1) +mmctx_data(0x404130, 3) +mmctx_data(0x404150, 3) +mmctx_data(0x404164, 2) +mmctx_data(0x404178, 2) +mmctx_data(0x404200, 8) +mmctx_data(0x404404, 14) +mmctx_data(0x404460, 4) +mmctx_data(0x404480, 1) +mmctx_data(0x404498, 1) +mmctx_data(0x404604, 4) +mmctx_data(0x404618, 32) +mmctx_data(0x404698, 21) +mmctx_data(0x4046f0, 2) +mmctx_data(0x404700, 22) +mmctx_data(0x405800, 1) +mmctx_data(0x405830, 3) +mmctx_data(0x405854, 1) +mmctx_data(0x405870, 4) +mmctx_data(0x405a00, 2) +mmctx_data(0x405a18, 1) +mmctx_data(0x406020, 1) +mmctx_data(0x406028, 4) +mmctx_data(0x4064a8, 2) +mmctx_data(0x4064b4, 5) +mmctx_data(0x407804, 1) +mmctx_data(0x40780c, 6) +mmctx_data(0x4078bc, 1) +mmctx_data(0x408000, 7) +mmctx_data(0x408064, 1) +mmctx_data(0x408800, 3) +mmctx_data(0x408900, 4) +mmctx_data(0x408980, 1) +nvd9_hub_mmio_tail: + .align 256 chan_data: chan_mmio_count: .b32 0 @@ -113,8 +158,8 @@ chan_mmio_address: .b32 0 .align 256 xfer_data: .b32 0 -.section nvc0_grhub_code -bra init +.section #nvc0_grhub_code +bra #init define(`include_code') include(`nvc0_graph.fuc') @@ -157,7 +202,7 @@ init: iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE // setup i0 handler, and route all interrupts to it - mov $r1 ih + mov $r1 #ih mov $iv0 $r1 mov $r1 0x400 iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH @@ -201,11 +246,11 @@ init: // fetch enabled GPC/ROP counts mov $r14 -0x69fc // 0x409604 sethi $r14 0x400000 - call nv_rd32 + call #nv_rd32 extr $r1 $r15 16:20 - st b32 D[$r0 + rop_count] $r1 + st b32 D[$r0 + #rop_count] $r1 and $r15 0x1f - st b32 D[$r0 + gpc_count] $r15 + st b32 D[$r0 + #gpc_count] $r15 // set BAR_REQMASK to GPC mask mov $r1 1 @@ -220,14 +265,14 @@ init: mov $r2 0x800 shl b32 $r2 6 iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r15 chipsets - 8 + mov $r15 #chipsets - 8 init_find_chipset: add b32 $r15 8 ld b32 $r3 D[$r15 + 0x00] cmpu b32 $r3 $r2 - bra e init_context + bra e #init_context cmpu b32 $r3 0 - bra ne init_find_chipset + bra ne #init_find_chipset // unknown chipset ret @@ -239,9 +284,9 @@ init: ld b16 $r14 D[$r15 + 4] ld b16 $r15 D[$r15 + 6] sethi $r14 0 - st b32 D[$r0 + hub_mmio_list_head] $r14 - st b32 D[$r0 + hub_mmio_list_tail] $r15 - call mmctx_size + st b32 D[$r0 + #hub_mmio_list_head] $r14 + st b32 D[$r0 + #hub_mmio_list_tail] $r15 + call #mmctx_size // set mmctx base addresses now so we don't have to do it later, // they don't (currently) ever change @@ -260,7 +305,7 @@ init: add b32 $r1 1 shl b32 $r1 8 mov b32 $r15 $r1 - call strand_ctx_init + call #strand_ctx_init add b32 $r1 $r15 // initialise each GPC in sequence by passing in the offset of its @@ -271,40 +316,40 @@ init: // when it has completed, and return the size of its context data // in GPCn_CC_SCRATCH[1] // - ld b32 $r3 D[$r0 + gpc_count] + ld b32 $r3 D[$r0 + #gpc_count] mov $r4 0x2000 sethi $r4 0x500000 init_gpc: // setup, and start GPC ucode running add b32 $r14 $r4 0x804 mov b32 $r15 $r1 - call nv_wr32 // CC_SCRATCH[1] = ctx offset + call #nv_wr32 // CC_SCRATCH[1] = ctx offset add b32 $r14 $r4 0x800 mov b32 $r15 $r2 - call nv_wr32 // CC_SCRATCH[0] = chipset + call #nv_wr32 // CC_SCRATCH[0] = chipset add b32 $r14 $r4 0x10c clear b32 $r15 - call nv_wr32 + call #nv_wr32 add b32 $r14 $r4 0x104 - call nv_wr32 // ENTRY + call #nv_wr32 // ENTRY add b32 $r14 $r4 0x100 mov $r15 2 // CTRL_START_TRIGGER - call nv_wr32 // CTRL + call #nv_wr32 // CTRL // wait for it to complete, and adjust context size add b32 $r14 $r4 0x800 init_gpc_wait: - call nv_rd32 + call #nv_rd32 xbit $r15 $r15 31 - bra e init_gpc_wait + bra e #init_gpc_wait add b32 $r14 $r4 0x804 - call nv_rd32 + call #nv_rd32 add b32 $r1 $r15 // next! add b32 $r4 0x8000 sub b32 $r3 1 - bra ne init_gpc + bra ne #init_gpc // save context size, and tell host we're ready mov $r2 0x800 @@ -322,13 +367,13 @@ main: // sleep until we have something to do bset $flags $p0 sleep $p0 - mov $r13 cmd_queue - call queue_get - bra $p1 main + mov $r13 #cmd_queue + call #queue_get + bra $p1 #main // context switch, requested by GPU? cmpu b32 $r14 0x4001 - bra ne main_not_ctx_switch + bra ne #main_not_ctx_switch trace_set(T_AUTO) mov $r1 0xb00 shl b32 $r1 6 @@ -336,39 +381,39 @@ main: iord $r1 I[$r1 + 0x000] // CHAN_CUR xbit $r3 $r1 31 - bra e chsw_no_prev + bra e #chsw_no_prev xbit $r3 $r2 31 - bra e chsw_prev_no_next + bra e #chsw_prev_no_next push $r2 mov b32 $r2 $r1 trace_set(T_SAVE) bclr $flags $p1 bset $flags $p2 - call ctx_xfer + call #ctx_xfer trace_clr(T_SAVE); pop $r2 trace_set(T_LOAD); bset $flags $p1 - call ctx_xfer + call #ctx_xfer trace_clr(T_LOAD); - bra chsw_done + bra #chsw_done chsw_prev_no_next: push $r2 mov b32 $r2 $r1 bclr $flags $p1 bclr $flags $p2 - call ctx_xfer + call #ctx_xfer pop $r2 mov $r1 0xb00 shl b32 $r1 6 iowr I[$r1] $r2 - bra chsw_done + bra #chsw_done chsw_no_prev: xbit $r3 $r2 31 - bra e chsw_done + bra e #chsw_done bset $flags $p1 bclr $flags $p2 - call ctx_xfer + call #ctx_xfer // ack the context switch request chsw_done: @@ -377,32 +422,32 @@ main: mov $r2 1 iowr I[$r1 + 0x000] $r2 // 0x409b0c trace_clr(T_AUTO) - bra main + bra #main // request to set current channel? (*not* a context switch) main_not_ctx_switch: cmpu b32 $r14 0x0001 - bra ne main_not_ctx_chan + bra ne #main_not_ctx_chan mov b32 $r2 $r15 - call ctx_chan - bra main_done + call #ctx_chan + bra #main_done // request to store current channel context? main_not_ctx_chan: cmpu b32 $r14 0x0002 - bra ne main_not_ctx_save + bra ne #main_not_ctx_save trace_set(T_SAVE) bclr $flags $p1 bclr $flags $p2 - call ctx_xfer + call #ctx_xfer trace_clr(T_SAVE) - bra main_done + bra #main_done main_not_ctx_save: shl b32 $r15 $r14 16 or $r15 E_BAD_COMMAND - call error - bra main + call #error + bra #main main_done: mov $r1 0x820 @@ -410,7 +455,7 @@ main: clear b32 $r2 bset $r2 31 iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 - bra main + bra #main // interrupt handler ih: @@ -427,13 +472,13 @@ ih: // incoming fifo command? iord $r10 I[$r0 + 0x200] // INTR and $r11 $r10 0x00000004 - bra e ih_no_fifo + bra e #ih_no_fifo // queue incoming fifo command for later processing mov $r11 0x1900 - mov $r13 cmd_queue + mov $r13 #cmd_queue iord $r14 I[$r11 + 0x100] // FIFO_CMD iord $r15 I[$r11 + 0x000] // FIFO_DATA - call queue_put + call #queue_put add b32 $r11 0x400 mov $r14 1 iowr I[$r11 + 0x000] $r14 // FIFO_ACK @@ -441,18 +486,18 @@ ih: // context switch request? ih_no_fifo: and $r11 $r10 0x00000100 - bra e ih_no_ctxsw + bra e #ih_no_ctxsw // enqueue a context switch for later processing - mov $r13 cmd_queue + mov $r13 #cmd_queue mov $r14 0x4001 - call queue_put + call #queue_put // anything we didn't handle, bring it to the host's attention ih_no_ctxsw: mov $r11 0x104 not b32 $r11 and $r11 $r10 $r11 - bra e ih_no_other + bra e #ih_no_other mov $r10 0xc1c shl b32 $r10 6 iowr I[$r10] $r11 // INTR_UP_SET @@ -478,11 +523,11 @@ ctx_4160s: mov $r14 0x4160 sethi $r14 0x400000 mov $r15 1 - call nv_wr32 + call #nv_wr32 ctx_4160s_wait: - call nv_rd32 + call #nv_rd32 xbit $r15 $r15 4 - bra e ctx_4160s_wait + bra e #ctx_4160s_wait ret // Without clearing again at end of xfer, some things cause PGRAPH @@ -492,7 +537,7 @@ ctx_4160c: mov $r14 0x4160 sethi $r14 0x400000 clear b32 $r15 - call nv_wr32 + call #nv_wr32 ret // Again, not real sure @@ -503,7 +548,7 @@ ctx_4170s: mov $r14 0x4170 sethi $r14 0x400000 or $r15 0x10 - call nv_wr32 + call #nv_wr32 ret // Waits for a ctx_4170s() call to complete @@ -511,9 +556,9 @@ ctx_4170s: ctx_4170w: mov $r14 0x4170 sethi $r14 0x400000 - call nv_rd32 + call #nv_rd32 and $r15 0x10 - bra ne ctx_4170w + bra ne #ctx_4170w ret // Disables various things, waits a bit, and re-enables them.. @@ -530,7 +575,7 @@ ctx_redswitch: mov $r15 8 ctx_redswitch_delay: sub b32 $r15 1 - bra ne ctx_redswitch_delay + bra ne #ctx_redswitch_delay mov $r15 0x770 iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL ret @@ -546,10 +591,10 @@ ctx_86c: iowr I[$r14] $r15 // HUB(0x86c) = val mov $r14 -0x75ec sethi $r14 0x400000 - call nv_wr32 // ROP(0xa14) = val + call #nv_wr32 // ROP(0xa14) = val mov $r14 -0x5794 sethi $r14 0x410000 - call nv_wr32 // GPC(0x86c) = val + call #nv_wr32 // GPC(0x86c) = val ret // ctx_load - load's a channel's ctxctl data, and selects its vm @@ -561,7 +606,7 @@ ctx_load: // switch to channel, somewhat magic in parts.. mov $r10 12 // DONE_UNK12 - call wait_donez + call #wait_donez mov $r1 0xa24 shl b32 $r1 6 iowr I[$r1 + 0x000] $r0 // 0x409a24 @@ -576,7 +621,7 @@ ctx_load: ctx_chan_wait_0: iord $r4 I[$r1 + 0x100] and $r4 0x1f - bra ne ctx_chan_wait_0 + bra ne #ctx_chan_wait_0 iowr I[$r3 + 0x000] $r2 // CHAN_CUR // load channel header, fetch PGRAPH context pointer @@ -595,19 +640,19 @@ ctx_load: sethi $r2 0x80000000 iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vram mov $r1 0x10 // chan + 0x0210 - mov $r2 xfer_data + mov $r2 #xfer_data sethi $r2 0x00020000 // 16 bytes xdld $r1 $r2 xdwait trace_clr(T_LCHAN) // update current context - ld b32 $r1 D[$r0 + xfer_data + 4] + ld b32 $r1 D[$r0 + #xfer_data + 4] shl b32 $r1 24 - ld b32 $r2 D[$r0 + xfer_data + 0] + ld b32 $r2 D[$r0 + #xfer_data + 0] shr b32 $r2 8 or $r1 $r2 - st b32 D[$r0 + ctx_current] $r1 + st b32 D[$r0 + #ctx_current] $r1 // set transfer base to start of context, and fetch context header trace_set(T_LCTXH) @@ -618,7 +663,7 @@ ctx_load: mov $r1 0xa20 shl b32 $r1 6 iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vm - mov $r1 chan_data + mov $r1 #chan_data sethi $r1 0x00060000 // 256 bytes xdld $r0 $r1 xdwait @@ -635,10 +680,10 @@ ctx_load: // In: $r2 channel address // ctx_chan: - call ctx_4160s - call ctx_load + call #ctx_4160s + call #ctx_load mov $r10 12 // DONE_UNK12 - call wait_donez + call #wait_donez mov $r1 0xa10 shl b32 $r1 6 mov $r2 5 @@ -646,8 +691,8 @@ ctx_chan: ctx_chan_wait: iord $r2 I[$r1 + 0x000] or $r2 $r2 - bra ne ctx_chan_wait - call ctx_4160c + bra ne #ctx_chan_wait + call #ctx_4160c ret // Execute per-context state overrides list @@ -661,7 +706,7 @@ ctx_chan: // ctx_mmio_exec: // set transfer base to be the mmio list - ld b32 $r3 D[$r0 + chan_mmio_address] + ld b32 $r3 D[$r0 + #chan_mmio_address] mov $r2 0xa04 shl b32 $r2 6 iowr I[$r2 + 0x000] $r3 // MEM_BASE @@ -670,31 +715,31 @@ ctx_mmio_exec: ctx_mmio_loop: // fetch next 256 bytes of mmio list if necessary and $r4 $r3 0xff - bra ne ctx_mmio_pull - mov $r5 xfer_data + bra ne #ctx_mmio_pull + mov $r5 #xfer_data sethi $r5 0x00060000 // 256 bytes xdld $r3 $r5 xdwait // execute a single list entry ctx_mmio_pull: - ld b32 $r14 D[$r4 + xfer_data + 0x00] - ld b32 $r15 D[$r4 + xfer_data + 0x04] - call nv_wr32 + ld b32 $r14 D[$r4 + #xfer_data + 0x00] + ld b32 $r15 D[$r4 + #xfer_data + 0x04] + call #nv_wr32 // next! add b32 $r3 8 sub b32 $r1 1 - bra ne ctx_mmio_loop + bra ne #ctx_mmio_loop // set transfer base back to the current context ctx_mmio_done: - ld b32 $r3 D[$r0 + ctx_current] + ld b32 $r3 D[$r0 + #ctx_current] iowr I[$r2 + 0x000] $r3 // MEM_BASE // disable the mmio list now, we don't need/want to execute it again - st b32 D[$r0 + chan_mmio_count] $r0 - mov $r1 chan_data + st b32 D[$r0 + #chan_mmio_count] $r0 + mov $r1 #chan_data sethi $r1 0x00060000 // 256 bytes xdst $r0 $r1 xdwait @@ -709,46 +754,46 @@ ctx_mmio_exec: // on load it means: "a save preceeded this load" // ctx_xfer: - bra not $p1 ctx_xfer_pre - bra $p2 ctx_xfer_pre_load + bra not $p1 #ctx_xfer_pre + bra $p2 #ctx_xfer_pre_load ctx_xfer_pre: mov $r15 0x10 - call ctx_86c - call ctx_4160s - bra not $p1 ctx_xfer_exec + call #ctx_86c + call #ctx_4160s + bra not $p1 #ctx_xfer_exec ctx_xfer_pre_load: mov $r15 2 - call ctx_4170s - call ctx_4170w - call ctx_redswitch + call #ctx_4170s + call #ctx_4170w + call #ctx_redswitch clear b32 $r15 - call ctx_4170s - call ctx_load + call #ctx_4170s + call #ctx_load // fetch context pointer, and initiate xfer on all GPCs ctx_xfer_exec: - ld b32 $r1 D[$r0 + ctx_current] + ld b32 $r1 D[$r0 + #ctx_current] mov $r2 0x414 shl b32 $r2 6 iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset mov $r14 -0x5b00 sethi $r14 0x410000 mov b32 $r15 $r1 - call nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer + call #nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer add b32 $r14 4 xbit $r15 $flags $p1 xbit $r2 $flags $p2 shl b32 $r2 1 or $r15 $r2 - call nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) + call #nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) // strands mov $r1 0x4afc sethi $r1 0x20000 mov $r2 0xc iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call strand_wait + call #strand_wait mov $r2 0x47fc sethi $r2 0x20000 iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 @@ -760,22 +805,22 @@ ctx_xfer: xbit $r10 $flags $p1 // direction or $r10 6 // first, last mov $r11 0 // base = 0 - ld b32 $r12 D[$r0 + hub_mmio_list_head] - ld b32 $r13 D[$r0 + hub_mmio_list_tail] + ld b32 $r12 D[$r0 + #hub_mmio_list_head] + ld b32 $r13 D[$r0 + #hub_mmio_list_tail] mov $r14 0 // not multi - call mmctx_xfer + call #mmctx_xfer // wait for GPCs to all complete mov $r10 8 // DONE_BAR - call wait_doneo + call #wait_doneo // wait for strand xfer to complete - call strand_wait + call #strand_wait // post-op - bra $p1 ctx_xfer_post + bra $p1 #ctx_xfer_post mov $r10 12 // DONE_UNK12 - call wait_donez + call #wait_donez mov $r1 0xa10 shl b32 $r1 6 mov $r2 5 @@ -783,27 +828,27 @@ ctx_xfer: ctx_xfer_post_save_wait: iord $r2 I[$r1] or $r2 $r2 - bra ne ctx_xfer_post_save_wait + bra ne #ctx_xfer_post_save_wait - bra $p2 ctx_xfer_done + bra $p2 #ctx_xfer_done ctx_xfer_post: mov $r15 2 - call ctx_4170s + call #ctx_4170s clear b32 $r15 - call ctx_86c - call strand_post - call ctx_4170w + call #ctx_86c + call #strand_post + call #ctx_4170w clear b32 $r15 - call ctx_4170s + call #ctx_4170s - bra not $p1 ctx_xfer_no_post_mmio - ld b32 $r1 D[$r0 + chan_mmio_count] + bra not $p1 #ctx_xfer_no_post_mmio + ld b32 $r1 D[$r0 + #chan_mmio_count] or $r1 $r1 - bra e ctx_xfer_no_post_mmio - call ctx_mmio_exec + bra e #ctx_xfer_no_post_mmio + call #ctx_mmio_exec ctx_xfer_no_post_mmio: - call ctx_4160c + call #ctx_4160c ctx_xfer_done: ret diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h index 241d3263f1e5..c5ed307abeb9 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h @@ -23,19 +23,21 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, 0x00000000, 0x000000c0, - 0x01340098, + 0x013c00a0, 0x000000c1, - 0x01380098, + 0x014000a0, 0x000000c3, - 0x01340098, + 0x013c00a0, 0x000000c4, - 0x01340098, + 0x013c00a0, 0x000000c8, - 0x01340098, + 0x013c00a0, 0x000000ce, - 0x01340098, + 0x013c00a0, 0x000000cf, - 0x01340098, + 0x013c00a0, + 0x000000d9, + 0x01dc0140, 0x00000000, 0x0417e91c, 0x04400204, @@ -77,47 +79,45 @@ uint32_t nvc0_grhub_data[] = { 0x0c408900, 0x00408980, 0x044064c0, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x0417e91c, + 0x04400204, + 0x24404004, + 0x00404044, + 0x34404094, + 0x184040d0, + 0x004040f8, + 0x08404130, + 0x08404150, + 0x04404164, + 0x04404178, + 0x1c404200, + 0x34404404, + 0x0c404460, + 0x00404480, + 0x00404498, + 0x0c404604, + 0x7c404618, + 0x50404698, + 0x044046f0, + 0x54404700, + 0x00405800, + 0x08405830, + 0x00405854, + 0x0c405870, + 0x04405a00, + 0x00405a18, + 0x00406020, + 0x0c406028, + 0x044064a8, + 0x104064b4, + 0x00407804, + 0x1440780c, + 0x004078bc, + 0x18408000, + 0x00408064, + 0x08408800, + 0x0c408900, + 0x00408980, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c index 929aded35cb5..e9992f62c1c0 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -153,3 +153,240 @@ nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) perflvl->vdec = read_clk(dev, 0x0e); return 0; } + +struct nvc0_pm_clock { + u32 freq; + u32 ssel; + u32 mdiv; + u32 dsrc; + u32 ddiv; + u32 coef; +}; + +struct nvc0_pm_state { + struct nvc0_pm_clock eng[16]; +}; + +static u32 +calc_div(struct drm_device *dev, int clk, u32 ref, u32 freq, u32 *ddiv) +{ + u32 div = min((ref * 2) / freq, (u32)65); + if (div < 2) + div = 2; + + *ddiv = div - 2; + return (ref * 2) / div; +} + +static u32 +calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv) +{ + u32 sclk; + + /* use one of the fixed frequencies if possible */ + *ddiv = 0x00000000; + switch (freq) { + case 27000: + case 108000: + *dsrc = 0x00000000; + if (freq == 108000) + *dsrc |= 0x00030000; + return freq; + case 100000: + *dsrc = 0x00000002; + return freq; + default: + *dsrc = 0x00000003; + break; + } + + /* otherwise, calculate the closest divider */ + sclk = read_vco(dev, clk); + if (clk < 7) + sclk = calc_div(dev, clk, sclk, freq, ddiv); + return sclk; +} + +static u32 +calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef) +{ + struct pll_lims limits; + int N, M, P, ret; + + ret = get_pll_limits(dev, 0x137000 + (clk * 0x20), &limits); + if (ret) + return 0; + + limits.refclk = read_div(dev, clk, 0x137120, 0x137140); + if (!limits.refclk) + return 0; + + ret = nva3_calc_pll(dev, &limits, freq, &N, NULL, &M, &P); + if (ret <= 0) + return 0; + + *coef = (P << 16) | (N << 8) | M; + return ret; +} + +/* A (likely rather simplified and incomplete) view of the clock tree + * + * Key: + * + * S: source select + * D: divider + * P: pll + * F: switch + * + * Engine clocks: + * + * 137250(D) ---- 137100(F0) ---- 137160(S)/1371d0(D) ------------------- ref + * (F1) ---- 1370X0(P) ---- 137120(S)/137140(D) ---- ref + * + * Not all registers exist for all clocks. For example: clocks >= 8 don't + * have their own PLL (all tied to clock 7's PLL when in PLL mode), nor do + * they have the divider at 1371d0, though the source selection at 137160 + * still exists. You must use the divider at 137250 for these instead. + * + * Memory clock: + * + * TBD, read_mem() above is likely very wrong... + * + */ + +static int +calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq) +{ + u32 src0, div0, div1D, div1P = 0; + u32 clk0, clk1 = 0; + + /* invalid clock domain */ + if (!freq) + return 0; + + /* first possible path, using only dividers */ + clk0 = calc_src(dev, clk, freq, &src0, &div0); + clk0 = calc_div(dev, clk, clk0, freq, &div1D); + + /* see if we can get any closer using PLLs */ + if (clk0 != freq) { + if (clk < 7) + clk1 = calc_pll(dev, clk, freq, &info->coef); + else + clk1 = read_pll(dev, 0x1370e0); + clk1 = calc_div(dev, clk, clk1, freq, &div1P); + } + + /* select the method which gets closest to target freq */ + if (abs((int)freq - clk0) <= abs((int)freq - clk1)) { + info->dsrc = src0; + if (div0) { + info->ddiv |= 0x80000000; + info->ddiv |= div0 << 8; + info->ddiv |= div0; + } + if (div1D) { + info->mdiv |= 0x80000000; + info->mdiv |= div1D; + } + info->ssel = 0; + info->freq = clk0; + } else { + if (div1P) { + info->mdiv |= 0x80000000; + info->mdiv |= div1P << 8; + } + info->ssel = (1 << clk); + info->freq = clk1; + } + + return 0; +} + +void * +nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_pm_state *info; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + /* NFI why this is still in the performance table, the ROPCs appear + * to get their clock from clock 2 ("hub07", actually hub05 on this + * chip, but, anyway...) as well. nvatiming confirms hub05 and ROP + * are always the same freq with the binary driver even when the + * performance table says they should differ. + */ + if (dev_priv->chipset == 0xd9) + perflvl->rop = 0; + + if ((ret = calc_clk(dev, 0x00, &info->eng[0x00], perflvl->shader)) || + (ret = calc_clk(dev, 0x01, &info->eng[0x01], perflvl->rop)) || + (ret = calc_clk(dev, 0x02, &info->eng[0x02], perflvl->hub07)) || + (ret = calc_clk(dev, 0x07, &info->eng[0x07], perflvl->hub06)) || + (ret = calc_clk(dev, 0x08, &info->eng[0x08], perflvl->hub01)) || + (ret = calc_clk(dev, 0x09, &info->eng[0x09], perflvl->copy)) || + (ret = calc_clk(dev, 0x0c, &info->eng[0x0c], perflvl->daemon)) || + (ret = calc_clk(dev, 0x0e, &info->eng[0x0e], perflvl->vdec))) { + kfree(info); + return ERR_PTR(ret); + } + + return info; +} + +static void +prog_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info) +{ + /* program dividers at 137160/1371d0 first */ + if (clk < 7 && !info->ssel) { + nv_mask(dev, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv); + nv_wr32(dev, 0x137160 + (clk * 0x04), info->dsrc); + } + + /* switch clock to non-pll mode */ + nv_mask(dev, 0x137100, (1 << clk), 0x00000000); + nv_wait(dev, 0x137100, (1 << clk), 0x00000000); + + /* reprogram pll */ + if (clk < 7) { + /* make sure it's disabled first... */ + u32 base = 0x137000 + (clk * 0x20); + u32 ctrl = nv_rd32(dev, base + 0x00); + if (ctrl & 0x00000001) { + nv_mask(dev, base + 0x00, 0x00000004, 0x00000000); + nv_mask(dev, base + 0x00, 0x00000001, 0x00000000); + } + /* program it to new values, if necessary */ + if (info->ssel) { + nv_wr32(dev, base + 0x04, info->coef); + nv_mask(dev, base + 0x00, 0x00000001, 0x00000001); + nv_wait(dev, base + 0x00, 0x00020000, 0x00020000); + nv_mask(dev, base + 0x00, 0x00020004, 0x00000004); + } + } + + /* select pll/non-pll mode, and program final clock divider */ + nv_mask(dev, 0x137100, (1 << clk), info->ssel); + nv_wait(dev, 0x137100, (1 << clk), info->ssel); + nv_mask(dev, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv); +} + +int +nvc0_pm_clocks_set(struct drm_device *dev, void *data) +{ + struct nvc0_pm_state *info = data; + int i; + + for (i = 0; i < 16; i++) { + if (!info->eng[i].freq) + continue; + prog_clk(dev, i, &info->eng[i]); + } + + kfree(info); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index cb006a718e70..d2ba2f07400b 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -35,12 +35,34 @@ #include "nouveau_fb.h" #include "nv50_display.h" +#define EVO_DMA_NR 9 + +#define EVO_MASTER (0x00) +#define EVO_FLIP(c) (0x01 + (c)) +#define EVO_OVLY(c) (0x05 + (c)) +#define EVO_OIMM(c) (0x09 + (c)) +#define EVO_CURS(c) (0x0d + (c)) + +/* offsets in shared sync bo of various structures */ +#define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) +#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) +#define EVO_FLIP_SEM0(c) EVO_SYNC((c), 0x00) +#define EVO_FLIP_SEM1(c) EVO_SYNC((c), 0x10) + +struct evo { + int idx; + dma_addr_t handle; + u32 *ptr; + struct { + u32 offset; + u16 value; + } sem; +}; + struct nvd0_display { struct nouveau_gpuobj *mem; - struct { - dma_addr_t handle; - u32 *ptr; - } evo[1]; + struct nouveau_bo *sync; + struct evo evo[9]; struct tasklet_struct tasklet; u32 modeset; @@ -53,6 +75,15 @@ nvd0_display(struct drm_device *dev) return dev_priv->engine.display.priv; } +static struct drm_crtc * +nvd0_display_crtc_get(struct drm_encoder *encoder) +{ + return nouveau_encoder(encoder)->crtc; +} + +/****************************************************************************** + * EVO channel helpers + *****************************************************************************/ static inline int evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data) { @@ -84,6 +115,9 @@ evo_wait(struct drm_device *dev, int id, int nr) put = 0; } + if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) + NV_INFO(dev, "Evo%d: %p START\n", id, disp->evo[id].ptr + put); + return disp->evo[id].ptr + put; } @@ -91,40 +125,264 @@ static void evo_kick(u32 *push, struct drm_device *dev, int id) { struct nvd0_display *disp = nvd0_display(dev); + + if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) { + u32 curp = nv_rd32(dev, 0x640000 + (id * 0x1000)) >> 2; + u32 *cur = disp->evo[id].ptr + curp; + + while (cur < push) + NV_INFO(dev, "Evo%d: 0x%08x\n", id, *cur++); + NV_INFO(dev, "Evo%d: %p KICK!\n", id, push); + } + nv_wr32(dev, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2); } #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) #define evo_data(p,d) *((p)++) = (d) -static struct drm_crtc * -nvd0_display_crtc_get(struct drm_encoder *encoder) +static int +evo_init_dma(struct drm_device *dev, int ch) { - return nouveau_encoder(encoder)->crtc; + struct nvd0_display *disp = nvd0_display(dev); + u32 flags; + + flags = 0x00000000; + if (ch == EVO_MASTER) + flags |= 0x01000000; + + nv_wr32(dev, 0x610494 + (ch * 0x0010), (disp->evo[ch].handle >> 8) | 3); + nv_wr32(dev, 0x610498 + (ch * 0x0010), 0x00010000); + nv_wr32(dev, 0x61049c + (ch * 0x0010), 0x00000001); + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010); + nv_wr32(dev, 0x640000 + (ch * 0x1000), 0x00000000); + nv_wr32(dev, 0x610490 + (ch * 0x0010), 0x00000013 | flags); + if (!nv_wait(dev, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000)) { + NV_ERROR(dev, "PDISP: ch%d 0x%08x\n", ch, + nv_rd32(dev, 0x610490 + (ch * 0x0010))); + return -EBUSY; + } + + nv_mask(dev, 0x610090, (1 << ch), (1 << ch)); + nv_mask(dev, 0x6100a0, (1 << ch), (1 << ch)); + return 0; +} + +static void +evo_fini_dma(struct drm_device *dev, int ch) +{ + if (!(nv_rd32(dev, 0x610490 + (ch * 0x0010)) & 0x00000010)) + return; + + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000000); + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000003, 0x00000000); + nv_wait(dev, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000); + nv_mask(dev, 0x610090, (1 << ch), 0x00000000); + nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000); +} + +static inline void +evo_piow(struct drm_device *dev, int ch, u16 mthd, u32 data) +{ + nv_wr32(dev, 0x640000 + (ch * 0x1000) + mthd, data); +} + +static int +evo_init_pio(struct drm_device *dev, int ch) +{ + nv_wr32(dev, 0x610490 + (ch * 0x0010), 0x00000001); + if (!nv_wait(dev, 0x610490 + (ch * 0x0010), 0x00010000, 0x00010000)) { + NV_ERROR(dev, "PDISP: ch%d 0x%08x\n", ch, + nv_rd32(dev, 0x610490 + (ch * 0x0010))); + return -EBUSY; + } + + nv_mask(dev, 0x610090, (1 << ch), (1 << ch)); + nv_mask(dev, 0x6100a0, (1 << ch), (1 << ch)); + return 0; +} + +static void +evo_fini_pio(struct drm_device *dev, int ch) +{ + if (!(nv_rd32(dev, 0x610490 + (ch * 0x0010)) & 0x00000001)) + return; + + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010); + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000001, 0x00000000); + nv_wait(dev, 0x610490 + (ch * 0x0010), 0x00010000, 0x00000000); + nv_mask(dev, 0x610090, (1 << ch), 0x00000000); + nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000); +} + +static bool +evo_sync_wait(void *data) +{ + return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000; +} + +static int +evo_sync(struct drm_device *dev, int ch) +{ + struct nvd0_display *disp = nvd0_display(dev); + u32 *push = evo_wait(dev, ch, 8); + if (push) { + nouveau_bo_wr32(disp->sync, EVO_MAST_NTFY, 0x00000000); + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x80000000 | EVO_MAST_NTFY); + evo_mthd(push, 0x0080, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_kick(push, dev, ch); + if (nv_wait_cb(dev, evo_sync_wait, disp->sync)) + return 0; + } + + return -EBUSY; +} + +/****************************************************************************** + * Page flipping channel + *****************************************************************************/ +struct nouveau_bo * +nvd0_display_crtc_sema(struct drm_device *dev, int crtc) +{ + return nvd0_display(dev)->sync; +} + +void +nvd0_display_flip_stop(struct drm_crtc *crtc) +{ + struct nvd0_display *disp = nvd0_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)]; + u32 *push; + + push = evo_wait(crtc->dev, evo->idx, 8); + if (push) { + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0094, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, crtc->dev, evo->idx); + } +} + +int +nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, + struct nouveau_channel *chan, u32 swap_interval) +{ + struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); + struct nvd0_display *disp = nvd0_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)]; + u64 offset; + u32 *push; + int ret; + + swap_interval <<= 4; + if (swap_interval == 0) + swap_interval |= 0x100; + + push = evo_wait(crtc->dev, evo->idx, 128); + if (unlikely(push == NULL)) + return -EBUSY; + + /* synchronise with the rendering channel, if necessary */ + if (likely(chan)) { + ret = RING_SPACE(chan, 10); + if (ret) + return ret; + + offset = chan->dispc_vma[nv_crtc->index].offset; + offset += evo->sem.offset; + + BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); + OUT_RING (chan, upper_32_bits(offset)); + OUT_RING (chan, lower_32_bits(offset)); + OUT_RING (chan, 0xf00d0000 | evo->sem.value); + OUT_RING (chan, 0x1002); + BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); + OUT_RING (chan, upper_32_bits(offset)); + OUT_RING (chan, lower_32_bits(offset ^ 0x10)); + OUT_RING (chan, 0x74b1e000); + OUT_RING (chan, 0x1001); + FIRE_RING (chan); + } else { + nouveau_bo_wr32(disp->sync, evo->sem.offset / 4, + 0xf00d0000 | evo->sem.value); + evo_sync(crtc->dev, EVO_MASTER); + } + + /* queue the flip */ + evo_mthd(push, 0x0100, 1); + evo_data(push, 0xfffe0000); + evo_mthd(push, 0x0084, 1); + evo_data(push, swap_interval); + if (!(swap_interval & 0x00000100)) { + evo_mthd(push, 0x00e0, 1); + evo_data(push, 0x40000000); + } + evo_mthd(push, 0x0088, 4); + evo_data(push, evo->sem.offset); + evo_data(push, 0xf00d0000 | evo->sem.value); + evo_data(push, 0x74b1e000); + evo_data(push, NvEvoSync); + evo_mthd(push, 0x00a0, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, nv_fb->r_dma); + evo_mthd(push, 0x0110, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0400, 5); + evo_data(push, nv_fb->nvbo->bo.offset >> 8); + evo_data(push, 0); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nv_fb->r_pitch); + evo_data(push, nv_fb->r_format); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, crtc->dev, evo->idx); + + evo->sem.offset ^= 0x10; + evo->sem.value++; + return 0; } /****************************************************************************** * CRTC *****************************************************************************/ static int -nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) +nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) { struct drm_device *dev = nv_crtc->base.dev; - u32 *push, mode; + struct nouveau_connector *nv_connector; + struct drm_connector *connector; + u32 *push, mode = 0x00; - mode = 0x00000000; - if (on) { - /* 0x11: 6bpc dynamic 2x2 - * 0x13: 8bpc dynamic 2x2 - * 0x19: 6bpc static 2x2 - * 0x1b: 8bpc static 2x2 - * 0x21: 6bpc temporal - * 0x23: 8bpc temporal - */ - mode = 0x00000011; + nv_connector = nouveau_crtc_connector_get(nv_crtc); + connector = &nv_connector->base; + if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { + if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = nv_connector->dithering_mode; + } + + if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { + if (connector->display_info.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= nv_connector->dithering_depth; } - push = evo_wait(dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0490 + (nv_crtc->index * 0x300), 1); evo_data(push, mode); @@ -132,63 +390,98 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); } - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } return 0; } static int -nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update) +nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) { - struct drm_display_mode *mode = &nv_crtc->base.mode; + struct drm_display_mode *omode, *umode = &nv_crtc->base.mode; struct drm_device *dev = nv_crtc->base.dev; + struct drm_crtc *crtc = &nv_crtc->base; struct nouveau_connector *nv_connector; - u32 *push, outX, outY; - - outX = mode->hdisplay; - outY = mode->vdisplay; + int mode = DRM_MODE_SCALE_NONE; + u32 oX, oY, *push; + /* start off at the resolution we programmed the crtc for, this + * effectively handles NONE/FULL scaling + */ nv_connector = nouveau_crtc_connector_get(nv_crtc); - if (nv_connector && nv_connector->native_mode) { - struct drm_display_mode *native = nv_connector->native_mode; - u32 xratio = (native->hdisplay << 19) / mode->hdisplay; - u32 yratio = (native->vdisplay << 19) / mode->vdisplay; - - switch (type) { - case DRM_MODE_SCALE_ASPECT: - if (xratio > yratio) { - outX = (mode->hdisplay * yratio) >> 19; - outY = (mode->vdisplay * yratio) >> 19; - } else { - outX = (mode->hdisplay * xratio) >> 19; - outY = (mode->vdisplay * xratio) >> 19; - } - break; - case DRM_MODE_SCALE_FULLSCREEN: - outX = native->hdisplay; - outY = native->vdisplay; - break; - default: - break; + if (nv_connector && nv_connector->native_mode) + mode = nv_connector->scaling_mode; + + if (mode != DRM_MODE_SCALE_NONE) + omode = nv_connector->native_mode; + else + omode = umode; + + oX = omode->hdisplay; + oY = omode->vdisplay; + if (omode->flags & DRM_MODE_FLAG_DBLSCAN) + oY *= 2; + + /* add overscan compensation if necessary, will keep the aspect + * ratio the same as the backend mode unless overridden by the + * user setting both hborder and vborder properties. + */ + if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || + (nv_connector->underscan == UNDERSCAN_AUTO && + nv_connector->edid && + drm_detect_hdmi_monitor(nv_connector->edid)))) { + u32 bX = nv_connector->underscan_hborder; + u32 bY = nv_connector->underscan_vborder; + u32 aspect = (oY << 19) / oX; + + if (bX) { + oX -= (bX * 2); + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; + } else { + oX -= (oX >> 4) + 32; + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; + } + } + + /* handle CENTER/ASPECT scaling, taking into account the areas + * removed already for overscan compensation + */ + switch (mode) { + case DRM_MODE_SCALE_CENTER: + oX = min((u32)umode->hdisplay, oX); + oY = min((u32)umode->vdisplay, oY); + /* fall-through */ + case DRM_MODE_SCALE_ASPECT: + if (oY < oX) { + u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; + oX = ((oY * aspect) + (aspect / 2)) >> 19; + } else { + u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; + oY = ((oX * aspect) + (aspect / 2)) >> 19; } + break; + default: + break; } - push = evo_wait(dev, 0, 16); + push = evo_wait(dev, EVO_MASTER, 8); if (push) { evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); - evo_data(push, (outY << 16) | outX); - evo_data(push, (outY << 16) | outX); - evo_data(push, (outY << 16) | outX); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1); - evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); + evo_data(push, (umode->vdisplay << 16) | umode->hdisplay); + evo_kick(push, dev, EVO_MASTER); if (update) { - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); + nvd0_display_flip_stop(crtc); + nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); } - evo_kick(push, dev, 0); } return 0; @@ -201,7 +494,7 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb); u32 *push; - push = evo_wait(fb->dev, 0, 16); + push = evo_wait(fb->dev, EVO_MASTER, 16); if (push) { evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1); evo_data(push, nvfb->nvbo->bo.offset >> 8); @@ -216,7 +509,7 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); } - evo_kick(push, fb->dev, 0); + evo_kick(push, fb->dev, EVO_MASTER); } nv_crtc->fb.tile_flags = nvfb->r_dma; @@ -227,7 +520,7 @@ static void nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update) { struct drm_device *dev = nv_crtc->base.dev; - u32 *push = evo_wait(dev, 0, 16); + u32 *push = evo_wait(dev, EVO_MASTER, 16); if (push) { if (show) { evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2); @@ -247,7 +540,7 @@ nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update) evo_data(push, 0x00000000); } - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } } @@ -262,7 +555,9 @@ nvd0_crtc_prepare(struct drm_crtc *crtc) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); u32 *push; - push = evo_wait(crtc->dev, 0, 2); + nvd0_display_flip_stop(crtc); + + push = evo_wait(crtc->dev, EVO_MASTER, 2); if (push) { evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); @@ -270,7 +565,7 @@ nvd0_crtc_prepare(struct drm_crtc *crtc) evo_data(push, 0x03000000); evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); - evo_kick(push, crtc->dev, 0); + evo_kick(push, crtc->dev, EVO_MASTER); } nvd0_crtc_cursor_show(nv_crtc, false, false); @@ -282,7 +577,7 @@ nvd0_crtc_commit(struct drm_crtc *crtc) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); u32 *push; - push = evo_wait(crtc->dev, 0, 32); + push = evo_wait(crtc->dev, EVO_MASTER, 32); if (push) { evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); evo_data(push, nv_crtc->fb.tile_flags); @@ -295,10 +590,11 @@ nvd0_crtc_commit(struct drm_crtc *crtc) evo_data(push, NvEvoVRAM); evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1); evo_data(push, 0xffffff00); - evo_kick(push, crtc->dev, 0); + evo_kick(push, crtc->dev, EVO_MASTER); } - nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true); + nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, false); + nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); } static bool @@ -333,21 +629,35 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_connector *nv_connector; - u32 htotal = mode->htotal; - u32 vtotal = mode->vtotal; - u32 hsyncw = mode->hsync_end - mode->hsync_start - 1; - u32 vsyncw = mode->vsync_end - mode->vsync_start - 1; - u32 hfrntp = mode->hsync_start - mode->hdisplay; - u32 vfrntp = mode->vsync_start - mode->vdisplay; - u32 hbackp = mode->htotal - mode->hsync_end; - u32 vbackp = mode->vtotal - mode->vsync_end; - u32 hss2be = hsyncw + hbackp; - u32 vss2be = vsyncw + vbackp; - u32 hss2de = htotal - hfrntp; - u32 vss2de = vtotal - vfrntp; + u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; + u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; + u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; + u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; + u32 vblan2e = 0, vblan2s = 1; + u32 magic = 0x31ec6000; u32 syncs, *push; int ret; + hactive = mode->htotal; + hsynce = mode->hsync_end - mode->hsync_start - 1; + hbackp = mode->htotal - mode->hsync_end; + hblanke = hsynce + hbackp; + hfrontp = mode->hsync_start - mode->hdisplay; + hblanks = mode->htotal - hfrontp - 1; + + vactive = mode->vtotal * vscan / ilace; + vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; + vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; + vblanke = vsynce + vbackp; + vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; + vblanks = vactive - vfrontp - 1; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vblan2e = vactive + vsynce + vbackp; + vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); + vactive = (vactive * 2) + 1; + magic |= 0x00000001; + } + syncs = 0x00000001; if (mode->flags & DRM_MODE_FLAG_NHSYNC) syncs |= 0x00000008; @@ -358,28 +668,33 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, if (ret) return ret; - push = evo_wait(crtc->dev, 0, 64); + push = evo_wait(crtc->dev, EVO_MASTER, 64); if (push) { - evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 5); + evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6); evo_data(push, 0x00000000); - evo_data(push, (vtotal << 16) | htotal); - evo_data(push, (vsyncw << 16) | hsyncw); - evo_data(push, (vss2be << 16) | hss2be); - evo_data(push, (vss2de << 16) | hss2de); + evo_data(push, (vactive << 16) | hactive); + evo_data(push, ( vsynce << 16) | hsynce); + evo_data(push, (vblanke << 16) | hblanke); + evo_data(push, (vblanks << 16) | hblanks); + evo_data(push, (vblan2e << 16) | vblan2s); evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); /* ??? */ evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3); evo_data(push, mode->clock * 1000); evo_data(push, 0x00200000); /* ??? */ evo_data(push, mode->clock * 1000); - evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 1); + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); evo_data(push, syncs); - evo_kick(push, crtc->dev, 0); + evo_data(push, magic); + evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2); + evo_data(push, 0x00000311); + evo_data(push, 0x00000100); + evo_kick(push, crtc->dev, EVO_MASTER); } nv_connector = nouveau_crtc_connector_get(nv_crtc); - nvd0_crtc_set_dither(nv_crtc, nv_connector->use_dithering, false); - nvd0_crtc_set_scale(nv_crtc, nv_connector->scaling_mode, false); + nvd0_crtc_set_dither(nv_crtc, false); + nvd0_crtc_set_scale(nv_crtc, false); nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, false); return 0; } @@ -400,7 +715,9 @@ nvd0_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, if (ret) return ret; + nvd0_display_flip_stop(crtc); nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, true); + nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); return 0; } @@ -410,6 +727,7 @@ nvd0_crtc_mode_set_base_atomic(struct drm_crtc *crtc, enum mode_set_atomic state) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + nvd0_display_flip_stop(crtc); nvd0_crtc_set_image(nv_crtc, fb, x, y, true); return 0; } @@ -472,10 +790,10 @@ static int nvd0_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - const u32 data = (y << 16) | x; + int ch = EVO_CURS(nv_crtc->index); - nv_wr32(crtc->dev, 0x64d084 + (nv_crtc->index * 0x1000), data); - nv_wr32(crtc->dev, 0x64d080 + (nv_crtc->index * 0x1000), 0x00000000); + evo_piow(crtc->dev, ch, 0x0084, (y << 16) | x); + evo_piow(crtc->dev, ch, 0x0080, 0x00000000); return 0; } @@ -525,6 +843,7 @@ static const struct drm_crtc_funcs nvd0_crtc_func = { .gamma_set = nvd0_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, .destroy = nvd0_crtc_destroy, + .page_flip = nouveau_crtc_page_flip, }; static void @@ -659,12 +978,12 @@ nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON); - push = evo_wait(encoder->dev, 0, 4); + push = evo_wait(encoder->dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 2); evo_data(push, 1 << nv_crtc->index); evo_data(push, 0x00ff); - evo_kick(push, encoder->dev, 0); + evo_kick(push, encoder->dev, EVO_MASTER); } nv_encoder->crtc = encoder->crtc; @@ -680,13 +999,13 @@ nvd0_dac_disconnect(struct drm_encoder *encoder) if (nv_encoder->crtc) { nvd0_crtc_prepare(nv_encoder->crtc); - push = evo_wait(dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 1); evo_data(push, 0x00000000); evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } nv_encoder->crtc = NULL; @@ -760,6 +1079,108 @@ nvd0_dac_create(struct drm_connector *connector, struct dcb_entry *dcbe) } /****************************************************************************** + * Audio + *****************************************************************************/ +static void +nvd0_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + int i, or = nv_encoder->or * 0x30; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_monitor_audio(nv_connector->edid)) + return; + + nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000001); + + drm_edid_to_eld(&nv_connector->base, nv_connector->edid); + if (nv_connector->base.eld[0]) { + u8 *eld = nv_connector->base.eld; + + for (i = 0; i < eld[2] * 4; i++) + nv_wr32(dev, 0x10ec00 + or, (i << 8) | eld[i]); + for (i = eld[2] * 4; i < 0x60; i++) + nv_wr32(dev, 0x10ec00 + or, (i << 8) | 0x00); + + nv_mask(dev, 0x10ec10 + or, 0x80000002, 0x80000002); + } +} + +static void +nvd0_audio_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + int or = nv_encoder->or * 0x30; + + nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000000); +} + +/****************************************************************************** + * HDMI + *****************************************************************************/ +static void +nvd0_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + int head = nv_crtc->index * 0x800; + u32 rekey = 56; /* binary driver, and tegra constant */ + u32 max_ac_packet; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_hdmi_monitor(nv_connector->edid)) + return; + + max_ac_packet = mode->htotal - mode->hdisplay; + max_ac_packet -= rekey; + max_ac_packet -= 18; /* constant from tegra */ + max_ac_packet /= 32; + + /* AVI InfoFrame */ + nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000); + nv_wr32(dev, 0x61671c + head, 0x000d0282); + nv_wr32(dev, 0x616720 + head, 0x0000006f); + nv_wr32(dev, 0x616724 + head, 0x00000000); + nv_wr32(dev, 0x616728 + head, 0x00000000); + nv_wr32(dev, 0x61672c + head, 0x00000000); + nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000001); + + /* ??? InfoFrame? */ + nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000); + nv_wr32(dev, 0x6167ac + head, 0x00000010); + nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000001); + + /* HDMI_CTRL */ + nv_mask(dev, 0x616798 + head, 0x401f007f, 0x40000000 | rekey | + max_ac_packet << 16); + + /* NFI, audio doesn't work without it though.. */ + nv_mask(dev, 0x616548 + head, 0x00000070, 0x00000000); + + nvd0_audio_mode_set(encoder, mode); +} + +static void +nvd0_hdmi_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + struct drm_device *dev = encoder->dev; + int head = nv_crtc->index * 0x800; + + nvd0_audio_disconnect(encoder); + + nv_mask(dev, 0x616798 + head, 0x40000000, 0x00000000); + nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000); + nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000); +} + +/****************************************************************************** * SOR *****************************************************************************/ static void @@ -829,7 +1250,8 @@ static void nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, struct drm_display_mode *mode) { - struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; + struct drm_device *dev = encoder->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_connector *nv_connector; @@ -852,6 +1274,8 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, or_config = (mode_ctrl & 0x00000f00) >> 8; if (mode->clock >= 165000) or_config |= 0x0100; + + nvd0_hdmi_mode_set(encoder, mode); break; case OUTPUT_LVDS: or_config = (mode_ctrl & 0x00000f00) >> 8; @@ -861,7 +1285,7 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, if (bios->fp.if_is_24bit) or_config |= 0x0200; } else { - if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { + if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { if (((u8 *)nv_connector->edid)[121] == 2) or_config |= 0x0100; } else @@ -889,12 +1313,12 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); - push = evo_wait(encoder->dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2); evo_data(push, mode_ctrl); evo_data(push, or_config); - evo_kick(push, encoder->dev, 0); + evo_kick(push, dev, EVO_MASTER); } nv_encoder->crtc = encoder->crtc; @@ -910,15 +1334,17 @@ nvd0_sor_disconnect(struct drm_encoder *encoder) if (nv_encoder->crtc) { nvd0_crtc_prepare(nv_encoder->crtc); - push = evo_wait(dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); evo_data(push, 0x00000000); evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } + nvd0_hdmi_disconnect(encoder); + nv_encoder->crtc = NULL; nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; } @@ -1159,6 +1585,12 @@ nvd0_display_intr(struct drm_device *dev) struct nvd0_display *disp = nvd0_display(dev); u32 intr = nv_rd32(dev, 0x610088); + if (intr & 0x00000001) { + u32 stat = nv_rd32(dev, 0x61008c); + nv_wr32(dev, 0x61008c, stat); + intr &= ~0x00000001; + } + if (intr & 0x00000002) { u32 stat = nv_rd32(dev, 0x61009c); int chid = ffs(stat) - 1; @@ -1215,38 +1647,29 @@ nvd0_display_intr(struct drm_device *dev) /****************************************************************************** * Init *****************************************************************************/ -static void +void nvd0_display_fini(struct drm_device *dev) { int i; - /* fini cursors */ - for (i = 14; i >= 13; i--) { - if (!(nv_rd32(dev, 0x610490 + (i * 0x10)) & 0x00000001)) - continue; - - nv_mask(dev, 0x610490 + (i * 0x10), 0x00000001, 0x00000000); - nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00000000); - nv_mask(dev, 0x610090, 1 << i, 0x00000000); - nv_mask(dev, 0x6100a0, 1 << i, 0x00000000); + /* fini cursors + overlays + flips */ + for (i = 1; i >= 0; i--) { + evo_fini_pio(dev, EVO_CURS(i)); + evo_fini_pio(dev, EVO_OIMM(i)); + evo_fini_dma(dev, EVO_OVLY(i)); + evo_fini_dma(dev, EVO_FLIP(i)); } /* fini master */ - if (nv_rd32(dev, 0x610490) & 0x00000010) { - nv_mask(dev, 0x610490, 0x00000010, 0x00000000); - nv_mask(dev, 0x610490, 0x00000003, 0x00000000); - nv_wait(dev, 0x610490, 0x80000000, 0x00000000); - nv_mask(dev, 0x610090, 0x00000001, 0x00000000); - nv_mask(dev, 0x6100a0, 0x00000001, 0x00000000); - } + evo_fini_dma(dev, EVO_MASTER); } int nvd0_display_init(struct drm_device *dev) { struct nvd0_display *disp = nvd0_display(dev); + int ret, i; u32 *push; - int i; if (nv_rd32(dev, 0x6100ac) & 0x00000100) { nv_wr32(dev, 0x6100ac, 0x00000100); @@ -1271,7 +1694,7 @@ nvd0_display_init(struct drm_device *dev) nv_wr32(dev, 0x6301c4 + (i * 0x800), sor); } - for (i = 0; i < 2; i++) { + for (i = 0; i < dev->mode_config.num_crtc; i++) { u32 crtc0 = nv_rd32(dev, 0x616104 + (i * 0x800)); u32 crtc1 = nv_rd32(dev, 0x616108 + (i * 0x800)); u32 crtc2 = nv_rd32(dev, 0x61610c + (i * 0x800)); @@ -1285,36 +1708,24 @@ nvd0_display_init(struct drm_device *dev) nv_mask(dev, 0x6100b0, 0x00000307, 0x00000307); /* init master */ - nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3); - nv_wr32(dev, 0x610498, 0x00010000); - nv_wr32(dev, 0x61049c, 0x00000001); - nv_mask(dev, 0x610490, 0x00000010, 0x00000010); - nv_wr32(dev, 0x640000, 0x00000000); - nv_wr32(dev, 0x610490, 0x01000013); - if (!nv_wait(dev, 0x610490, 0x80000000, 0x00000000)) { - NV_ERROR(dev, "PDISP: master 0x%08x\n", - nv_rd32(dev, 0x610490)); - return -EBUSY; + ret = evo_init_dma(dev, EVO_MASTER); + if (ret) + goto error; + + /* init flips + overlays + cursors */ + for (i = 0; i < dev->mode_config.num_crtc; i++) { + if ((ret = evo_init_dma(dev, EVO_FLIP(i))) || + (ret = evo_init_dma(dev, EVO_OVLY(i))) || + (ret = evo_init_pio(dev, EVO_OIMM(i))) || + (ret = evo_init_pio(dev, EVO_CURS(i)))) + goto error; } - nv_mask(dev, 0x610090, 0x00000001, 0x00000001); - nv_mask(dev, 0x6100a0, 0x00000001, 0x00000001); - /* init cursors */ - for (i = 13; i <= 14; i++) { - nv_wr32(dev, 0x610490 + (i * 0x10), 0x00000001); - if (!nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00010000)) { - NV_ERROR(dev, "PDISP: curs%d 0x%08x\n", i, - nv_rd32(dev, 0x610490 + (i * 0x10))); - return -EBUSY; - } - - nv_mask(dev, 0x610090, 1 << i, 1 << i); - nv_mask(dev, 0x6100a0, 1 << i, 1 << i); + push = evo_wait(dev, EVO_MASTER, 32); + if (!push) { + ret = -EBUSY; + goto error; } - - push = evo_wait(dev, 0, 32); - if (!push) - return -EBUSY; evo_mthd(push, 0x0088, 1); evo_data(push, NvEvoSync); evo_mthd(push, 0x0084, 1); @@ -1323,9 +1734,12 @@ nvd0_display_init(struct drm_device *dev) evo_data(push, 0x80000000); evo_mthd(push, 0x008c, 1); evo_data(push, 0x00000000); - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); - return 0; +error: + if (ret) + nvd0_display_fini(dev); + return ret; } void @@ -1334,11 +1748,16 @@ nvd0_display_destroy(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvd0_display *disp = nvd0_display(dev); struct pci_dev *pdev = dev->pdev; + int i; - nvd0_display_fini(dev); + for (i = 0; i < EVO_DMA_NR; i++) { + struct evo *evo = &disp->evo[i]; + pci_free_consistent(pdev, PAGE_SIZE, evo->ptr, evo->handle); + } - pci_free_consistent(pdev, PAGE_SIZE, disp->evo[0].ptr, disp->evo[0].handle); nouveau_gpuobj_ref(NULL, &disp->mem); + nouveau_bo_unmap(disp->sync); + nouveau_bo_ref(NULL, &disp->sync); nouveau_irq_unregister(dev, 26); dev_priv->engine.display.priv = NULL; @@ -1410,61 +1829,83 @@ nvd0_display_create(struct drm_device *dev) tasklet_init(&disp->tasklet, nvd0_display_bh, (unsigned long)dev); nouveau_irq_register(dev, 26, nvd0_display_intr); + /* small shared memory area we use for notifiers and semaphores */ + ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, &disp->sync); + if (!ret) { + ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM); + if (!ret) + ret = nouveau_bo_map(disp->sync); + if (ret) + nouveau_bo_ref(NULL, &disp->sync); + } + + if (ret) + goto out; + /* hash table and dma objects for the memory areas we care about */ ret = nouveau_gpuobj_new(dev, NULL, 0x4000, 0x10000, NVOBJ_FLAG_ZERO_ALLOC, &disp->mem); if (ret) goto out; - nv_wo32(disp->mem, 0x1000, 0x00000049); - nv_wo32(disp->mem, 0x1004, (disp->mem->vinst + 0x2000) >> 8); - nv_wo32(disp->mem, 0x1008, (disp->mem->vinst + 0x2fff) >> 8); - nv_wo32(disp->mem, 0x100c, 0x00000000); - nv_wo32(disp->mem, 0x1010, 0x00000000); - nv_wo32(disp->mem, 0x1014, 0x00000000); - nv_wo32(disp->mem, 0x0000, NvEvoSync); - nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001); - - nv_wo32(disp->mem, 0x1020, 0x00000049); - nv_wo32(disp->mem, 0x1024, 0x00000000); - nv_wo32(disp->mem, 0x1028, (dev_priv->vram_size - 1) >> 8); - nv_wo32(disp->mem, 0x102c, 0x00000000); - nv_wo32(disp->mem, 0x1030, 0x00000000); - nv_wo32(disp->mem, 0x1034, 0x00000000); - nv_wo32(disp->mem, 0x0008, NvEvoVRAM); - nv_wo32(disp->mem, 0x000c, (0x1020 << 9) | 0x00000001); - - nv_wo32(disp->mem, 0x1040, 0x00000009); - nv_wo32(disp->mem, 0x1044, 0x00000000); - nv_wo32(disp->mem, 0x1048, (dev_priv->vram_size - 1) >> 8); - nv_wo32(disp->mem, 0x104c, 0x00000000); - nv_wo32(disp->mem, 0x1050, 0x00000000); - nv_wo32(disp->mem, 0x1054, 0x00000000); - nv_wo32(disp->mem, 0x0010, NvEvoVRAM_LP); - nv_wo32(disp->mem, 0x0014, (0x1040 << 9) | 0x00000001); - - nv_wo32(disp->mem, 0x1060, 0x0fe00009); - nv_wo32(disp->mem, 0x1064, 0x00000000); - nv_wo32(disp->mem, 0x1068, (dev_priv->vram_size - 1) >> 8); - nv_wo32(disp->mem, 0x106c, 0x00000000); - nv_wo32(disp->mem, 0x1070, 0x00000000); - nv_wo32(disp->mem, 0x1074, 0x00000000); - nv_wo32(disp->mem, 0x0018, NvEvoFB32); - nv_wo32(disp->mem, 0x001c, (0x1060 << 9) | 0x00000001); - - pinstmem->flush(dev); + /* create evo dma channels */ + for (i = 0; i < EVO_DMA_NR; i++) { + struct evo *evo = &disp->evo[i]; + u64 offset = disp->sync->bo.offset; + u32 dmao = 0x1000 + (i * 0x100); + u32 hash = 0x0000 + (i * 0x040); + + evo->idx = i; + evo->sem.offset = EVO_SYNC(evo->idx, 0x00); + evo->ptr = pci_alloc_consistent(pdev, PAGE_SIZE, &evo->handle); + if (!evo->ptr) { + ret = -ENOMEM; + goto out; + } - /* push buffers for evo channels */ - disp->evo[0].ptr = - pci_alloc_consistent(pdev, PAGE_SIZE, &disp->evo[0].handle); - if (!disp->evo[0].ptr) { - ret = -ENOMEM; - goto out; + nv_wo32(disp->mem, dmao + 0x00, 0x00000049); + nv_wo32(disp->mem, dmao + 0x04, (offset + 0x0000) >> 8); + nv_wo32(disp->mem, dmao + 0x08, (offset + 0x0fff) >> 8); + nv_wo32(disp->mem, dmao + 0x0c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x10, 0x00000000); + nv_wo32(disp->mem, dmao + 0x14, 0x00000000); + nv_wo32(disp->mem, hash + 0x00, NvEvoSync); + nv_wo32(disp->mem, hash + 0x04, 0x00000001 | (i << 27) | + ((dmao + 0x00) << 9)); + + nv_wo32(disp->mem, dmao + 0x20, 0x00000049); + nv_wo32(disp->mem, dmao + 0x24, 0x00000000); + nv_wo32(disp->mem, dmao + 0x28, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, dmao + 0x2c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x30, 0x00000000); + nv_wo32(disp->mem, dmao + 0x34, 0x00000000); + nv_wo32(disp->mem, hash + 0x08, NvEvoVRAM); + nv_wo32(disp->mem, hash + 0x0c, 0x00000001 | (i << 27) | + ((dmao + 0x20) << 9)); + + nv_wo32(disp->mem, dmao + 0x40, 0x00000009); + nv_wo32(disp->mem, dmao + 0x44, 0x00000000); + nv_wo32(disp->mem, dmao + 0x48, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, dmao + 0x4c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x50, 0x00000000); + nv_wo32(disp->mem, dmao + 0x54, 0x00000000); + nv_wo32(disp->mem, hash + 0x10, NvEvoVRAM_LP); + nv_wo32(disp->mem, hash + 0x14, 0x00000001 | (i << 27) | + ((dmao + 0x40) << 9)); + + nv_wo32(disp->mem, dmao + 0x60, 0x0fe00009); + nv_wo32(disp->mem, dmao + 0x64, 0x00000000); + nv_wo32(disp->mem, dmao + 0x68, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, dmao + 0x6c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x70, 0x00000000); + nv_wo32(disp->mem, dmao + 0x74, 0x00000000); + nv_wo32(disp->mem, hash + 0x18, NvEvoFB32); + nv_wo32(disp->mem, hash + 0x1c, 0x00000001 | (i << 27) | + ((dmao + 0x60) << 9)); } - ret = nvd0_display_init(dev); - if (ret) - goto out; + pinstmem->flush(dev); out: if (ret) |