diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-05-03 09:17:01 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-05-03 09:17:01 +0200 |
commit | 53ba4f2fa73225113a488584df0d85d3cba52943 (patch) | |
tree | d85b984d9818abc3ccc0237eb53b710d9e96c39e /drivers/gpu/drm/nouveau | |
parent | lockstat: Make lockstat counting per cpu (diff) | |
parent | Linux 2.6.34-rc6 (diff) | |
download | linux-53ba4f2fa73225113a488584df0d85d3cba52943.tar.xz linux-53ba4f2fa73225113a488584df0d85d3cba52943.zip |
Merge commit 'v2.6.34-rc6' into core/locking
Diffstat (limited to 'drivers/gpu/drm/nouveau')
50 files changed, 4487 insertions, 1180 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 48c290b5da8c..453df3f6053f 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -12,17 +12,17 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_dp.o nouveau_grctx.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ - nv04_fb.o nv10_fb.o nv40_fb.o \ + nv04_fb.o nv10_fb.o nv40_fb.o nv50_fb.o \ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ nv40_graph.o nv50_graph.o \ - nv40_grctx.o \ + nv40_grctx.o nv50_grctx.o \ nv04_instmem.o nv50_instmem.o \ nv50_crtc.o nv50_dac.o nv50_sor.o \ nv50_cursor.o nv50_display.o nv50_fbcon.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 nv04_fbcon.o \ - nv17_gpio.o + nv17_gpio.o nv50_gpio.o nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 48227e744753..e13f6af0037a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -1,5 +1,6 @@ #include <linux/pci.h> #include <linux/acpi.h> +#include <linux/slab.h> #include <acpi/acpi_drivers.h> #include <acpi/acpi_bus.h> @@ -11,6 +12,8 @@ #include "nouveau_drm.h" #include "nv50_display.h" +#include <linux/vga_switcheroo.h> + #define NOUVEAU_DSM_SUPPORTED 0x00 #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 @@ -28,31 +31,30 @@ #define NOUVEAU_DSM_POWER_SPEED 0x01 #define NOUVEAU_DSM_POWER_STAMINA 0x02 -static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) -{ - static char muid[] = { - 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, - 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, - }; +static struct nouveau_dsm_priv { + bool dsm_detected; + acpi_handle dhandle; + acpi_handle dsm_handle; +} nouveau_dsm_priv; + +static const char nouveau_dsm_muid[] = { + 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, + 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, +}; - struct pci_dev *pdev = dev->pdev; - struct acpi_handle *handle; +static int nouveau_dsm(acpi_handle handle, int func, int arg, int *result) +{ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_object_list input; union acpi_object params[4]; union acpi_object *obj; int err; - handle = DEVICE_ACPI_HANDLE(&pdev->dev); - - if (!handle) - return -ENODEV; - input.count = 4; input.pointer = params; params[0].type = ACPI_TYPE_BUFFER; - params[0].buffer.length = sizeof(muid); - params[0].buffer.pointer = (char *)muid; + params[0].buffer.length = sizeof(nouveau_dsm_muid); + params[0].buffer.pointer = (char *)nouveau_dsm_muid; params[1].type = ACPI_TYPE_INTEGER; params[1].integer.value = 0x00000102; params[2].type = ACPI_TYPE_INTEGER; @@ -62,7 +64,7 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) err = acpi_evaluate_object(handle, "_DSM", &input, &output); if (err) { - NV_INFO(dev, "failed to evaluate _DSM: %d\n", err); + printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); return err; } @@ -86,40 +88,119 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) return 0; } -int nouveau_hybrid_setup(struct drm_device *dev) +static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) { - int result; - - if (nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STATE, - &result)) - return -ENODEV; - - NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result); - - if (result) { /* Ensure that the external GPU is enabled */ - nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL); - nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED, - NULL); - } else { /* Stamina mode - disable the external GPU */ - nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA, - NULL); - nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA, - NULL); - } + return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); +} + +static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) +{ + int arg; + if (state == VGA_SWITCHEROO_ON) + arg = NOUVEAU_DSM_POWER_SPEED; + else + arg = NOUVEAU_DSM_POWER_STAMINA; + nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); + return 0; +} + +static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) + return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_STAMINA); + else + return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_SPEED); +} +static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dsm_handle, state); +} + +static int nouveau_dsm_init(void) +{ return 0; } -bool nouveau_dsm_probe(struct drm_device *dev) +static int nouveau_dsm_get_client_id(struct pci_dev *pdev) { - int support = 0; + if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) + return VGA_SWITCHEROO_IGD; + else + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler nouveau_dsm_handler = { + .switchto = nouveau_dsm_switchto, + .power_state = nouveau_dsm_power_state, + .init = nouveau_dsm_init, + .get_client_id = nouveau_dsm_get_client_id, +}; - if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED, - NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support)) +static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) +{ + acpi_handle dhandle, nvidia_handle; + acpi_status status; + int ret; + uint32_t result; + + dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); + if (!dhandle) + return false; + status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); + if (ACPI_FAILURE(status)) { return false; + } - if (!support) + ret= nouveau_dsm(nvidia_handle, NOUVEAU_DSM_SUPPORTED, + NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); + if (ret < 0) return false; + nouveau_dsm_priv.dhandle = dhandle; + nouveau_dsm_priv.dsm_handle = nvidia_handle; return true; } + +static bool nouveau_dsm_detect(void) +{ + char acpi_method_name[255] = { 0 }; + struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; + struct pci_dev *pdev = NULL; + int has_dsm = 0; + int vga_count = 0; + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { + vga_count++; + + has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); + } + + if (vga_count == 2 && has_dsm) { + acpi_get_name(nouveau_dsm_priv.dsm_handle, ACPI_FULL_PATHNAME, &buffer); + printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", + acpi_method_name); + nouveau_dsm_priv.dsm_detected = true; + return true; + } + return false; +} + +void nouveau_register_dsm_handler(void) +{ + bool r; + + r = nouveau_dsm_detect(); + if (!r) + return; + + vga_switcheroo_register_handler(&nouveau_dsm_handler); +} + +void nouveau_unregister_dsm_handler(void) +{ + vga_switcheroo_unregister_handler(); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 20564f8cb0ec..406228f4a2a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -89,19 +89,21 @@ static struct backlight_ops nv50_bl_ops = { static int nouveau_nv40_backlight_init(struct drm_device *dev) { + struct backlight_properties props; struct drm_nouveau_private *dev_priv = dev->dev_private; struct backlight_device *bd; if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) return 0; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 31; bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev, - &nv40_bl_ops); + &nv40_bl_ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); dev_priv->backlight = bd; - bd->props.max_brightness = 31; bd->props.brightness = nv40_get_intensity(bd); backlight_update_status(bd); @@ -110,19 +112,21 @@ static int nouveau_nv40_backlight_init(struct drm_device *dev) static int nouveau_nv50_backlight_init(struct drm_device *dev) { + struct backlight_properties props; struct drm_nouveau_private *dev_priv = dev->dev_private; struct backlight_device *bd; if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT)) return 0; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 1025; bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev, - &nv50_bl_ops); + &nv50_bl_ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); dev_priv->backlight = bd; - bd->props.max_brightness = 1025; bd->props.brightness = nv50_get_intensity(bd); backlight_update_status(bd); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 0e9cd1d49130..abc382a9918b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -311,11 +311,11 @@ valid_reg(struct nvbios *bios, uint32_t reg) /* C51 has misaligned regs on purpose. Marvellous */ if (reg & 0x2 || - (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51)) + (reg & 0x1 && dev_priv->vbios.chip_version != 0x51)) NV_ERROR(dev, "======= misaligned reg 0x%08X =======\n", reg); /* warn on C51 regs that haven't been verified accessible in tracing */ - if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 && + if (reg & 0x1 && dev_priv->vbios.chip_version == 0x51 && reg != 0x130d && reg != 0x1311 && reg != 0x60081d) NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n", reg); @@ -420,7 +420,7 @@ bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data) LOG_OLD_VALUE(bios_rd32(bios, reg)); BIOSLOG(bios, " Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data); - if (dev_priv->VBIOS.execute) { + if (dev_priv->vbios.execute) { still_alive(); nv_wr32(bios->dev, reg, data); } @@ -647,7 +647,7 @@ nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk) reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16); reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1; - if (dev_priv->VBIOS.execute) { + if (dev_priv->vbios.execute) { still_alive(); nv_wr32(dev, reg + 4, reg1); nv_wr32(dev, reg + 0, reg0); @@ -689,7 +689,7 @@ setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk) static int dcb_entry_idx_from_crtchead(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; /* * For the results of this function to be correct, CR44 must have been @@ -700,7 +700,7 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev) uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0); - if (dcb_entry > bios->bdcb.dcb.entries) { + if (dcb_entry > bios->dcb.entries) { NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently " "(%02X)\n", dcb_entry); dcb_entry = 0x7f; /* unused / invalid marker */ @@ -713,25 +713,26 @@ 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 bios_parsed_dcb *bdcb = &dev_priv->VBIOS.bdcb; + struct dcb_table *dcb = &dev_priv->vbios.dcb; if (i2c_index == 0xff) { /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */ int idx = dcb_entry_idx_from_crtchead(dev), shift = 0; - int default_indices = bdcb->i2c_default_indices; + int default_indices = dcb->i2c_default_indices; - if (idx != 0x7f && bdcb->dcb.entry[idx].i2c_upper_default) + if (idx != 0x7f && dcb->entry[idx].i2c_upper_default) shift = 4; i2c_index = (default_indices >> shift) & 0xf; } if (i2c_index == 0x80) /* g80+ */ - i2c_index = bdcb->i2c_default_indices & 0xf; + i2c_index = dcb->i2c_default_indices & 0xf; return nouveau_i2c_find(dev, i2c_index); } -static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv) +static uint32_t +get_tmds_index_reg(struct drm_device *dev, uint8_t mlv) { /* * For mlv < 0x80, it is an index into a table of TMDS base addresses. @@ -744,6 +745,7 @@ static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv) */ struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; const int pramdac_offset[13] = { 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 }; const uint32_t pramdac_table[4] = { @@ -756,13 +758,12 @@ static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv) dcb_entry = dcb_entry_idx_from_crtchead(dev); if (dcb_entry == 0x7f) return 0; - dacoffset = pramdac_offset[ - dev_priv->VBIOS.bdcb.dcb.entry[dcb_entry].or]; + dacoffset = pramdac_offset[bios->dcb.entry[dcb_entry].or]; if (mlv == 0x81) dacoffset ^= 8; return 0x6808b0 + dacoffset; } else { - if (mlv > ARRAY_SIZE(pramdac_table)) { + if (mlv >= ARRAY_SIZE(pramdac_table)) { NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n", mlv); return 0; @@ -2572,48 +2573,34 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) * each GPIO according to various values listed in each entry */ - const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; + struct drm_nouveau_private *dev_priv = bios->dev->dev_private; const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; - const uint8_t *gpio_table = &bios->data[bios->bdcb.gpio_table_ptr]; - const uint8_t *gpio_entry; int i; - if (!iexec->execute) - return 1; - - if (bios->bdcb.version != 0x40) { - NV_ERROR(bios->dev, "DCB table not version 4.0\n"); - return 0; + if (dev_priv->card_type != NV_50) { + NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n"); + return -ENODEV; } - if (!bios->bdcb.gpio_table_ptr) { - NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n"); - return 0; - } + if (!iexec->execute) + return 1; - gpio_entry = gpio_table + gpio_table[1]; - for (i = 0; i < gpio_table[2]; i++, gpio_entry += gpio_table[3]) { - uint32_t entry = ROM32(gpio_entry[0]), r, s, v; - int line = (entry & 0x0000001f); + for (i = 0; i < bios->dcb.gpio.entries; i++) { + struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i]; + uint32_t r, s, v; - BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, entry); + BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry); - if ((entry & 0x0000ff00) == 0x0000ff00) - continue; + nv50_gpio_set(bios->dev, gpio->tag, gpio->state_default); - r = nv50_gpio_reg[line >> 3]; - s = (line & 0x07) << 2; - v = bios_rd32(bios, r) & ~(0x00000003 << s); - if (entry & 0x01000000) - v |= (((entry & 0x60000000) >> 29) ^ 2) << s; - else - v |= (((entry & 0x18000000) >> 27) ^ 2) << s; - bios_wr32(bios, r, v); - - r = nv50_gpio_ctl[line >> 4]; - s = (line & 0x0f); + /* The NVIDIA binary driver doesn't appear to actually do + * any of this, my VBIOS does however. + */ + /* 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 ((entry & 0x06000000) >> 25) { + switch ((gpio->entry & 0x06000000) >> 25) { case 1: v |= (0x00000001 << s); break; @@ -3123,7 +3110,7 @@ run_digital_op_script(struct drm_device *dev, uint16_t scriptptr, struct dcb_entry *dcbent, int head, bool dl) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; struct init_exec iexec = {true, false}; NV_TRACE(dev, "0x%04X: Parsing digital output script table\n", @@ -3140,7 +3127,7 @@ run_digital_op_script(struct drm_device *dev, uint16_t scriptptr, static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & OUTPUT_C ? 1 : 0); uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]); @@ -3194,10 +3181,9 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int * of a list of pxclks and script pointers. */ struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; unsigned int outputset = (dcbent->or == 4) ? 1 : 0; uint16_t scriptptr = 0, clktable; - uint8_t clktableptr = 0; /* * For now we assume version 3.0 table - g80 support will need some @@ -3216,26 +3202,29 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]); break; case LVDS_RESET: + clktable = bios->fp.lvdsmanufacturerpointer + 15; + if (dcbent->or == 4) + clktable += 8; + if (dcbent->lvdsconf.use_straps_for_mode) { if (bios->fp.dual_link) - clktableptr += 2; - if (bios->fp.BITbit1) - clktableptr++; + clktable += 4; + if (bios->fp.if_is_24bit) + clktable += 2; } else { /* using EDID */ - uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4]; - int fallbackcmpval = (dcbent->or == 4) ? 4 : 1; + int cmpval_24bit = (dcbent->or == 4) ? 4 : 1; if (bios->fp.dual_link) { - clktableptr += 2; - fallbackcmpval *= 2; + clktable += 4; + cmpval_24bit <<= 1; } - if (fallbackcmpval & fallback) - clktableptr++; + + if (bios->fp.strapless_is_24bit & cmpval_24bit) + clktable += 2; } - /* adding outputset * 8 may not be correct */ - clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]); + clktable = ROM16(bios->data[clktable]); if (!clktable) { NV_ERROR(dev, "Pixel clock comparison table not found\n"); return -ENOENT; @@ -3261,7 +3250,7 @@ int call_lvds_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, */ struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer]; uint32_t sel_clk_binding, sel_clk; int ret; @@ -3395,7 +3384,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios) #ifndef __powerpc__ NV_ERROR(dev, "Pointer to flat panel table invalid\n"); #endif - bios->pub.digital_min_front_porch = 0x4b; + bios->digital_min_front_porch = 0x4b; return 0; } @@ -3428,7 +3417,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios) * fptable[4] is the minimum * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap */ - bios->pub.digital_min_front_porch = fptable[4]; + bios->digital_min_front_porch = fptable[4]; ofs = -7; break; default: @@ -3467,7 +3456,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios) /* nv4x cards need both a strap value and fpindex of 0xf to use DDC */ if (lth.lvds_ver > 0x10) - bios->pub.fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf; + bios->fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf; /* * If either the strap or xlated fpindex value are 0xf there is no @@ -3491,7 +3480,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios) bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr]; if (!mode) /* just checking whether we can produce a mode */ @@ -3544,7 +3533,7 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b * at which modes should be set up in the dual link style. * * Following the header, the BMP (ver 0xa) table has several records, - * indexed by a seperate xlat table, indexed in turn by the fp strap in + * indexed by a separate xlat table, indexed in turn by the fp strap in * EXTDEV_BOOT. Each record had a config byte, followed by 6 script * numbers for use by INIT_SUB which controlled panel init and power, * and finally a dword of ms to sleep between power off and on @@ -3562,11 +3551,11 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b * until later, when this function should be called with non-zero pxclk */ struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0; struct lvdstableheader lth; uint16_t lvdsofs; - int ret, chip_version = bios->pub.chip_version; + int ret, chip_version = bios->chip_version; ret = parse_lvds_manufacturer_table_header(dev, bios, <h); if (ret) @@ -3637,37 +3626,40 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b *if_is_24bit = bios->data[lvdsofs] & 16; break; case 0x30: - /* - * My money would be on there being a 24 bit interface bit in - * this table, but I have no example of a laptop bios with a - * 24 bit panel to confirm that. Hence we shout loudly if any - * bit other than bit 0 is set (I've not even seen bit 1) - */ - if (bios->data[lvdsofs] > 1) - NV_ERROR(dev, - "You have a very unusual laptop display; please report it\n"); + case 0x40: /* * No sign of the "power off for reset" or "reset for panel * on" bits, but it's safer to assume we should */ bios->fp.power_off_for_reset = true; bios->fp.reset_after_pclk_change = true; + /* * It's ok lvdsofs is wrong for nv4x edid case; dual_link is - * over-written, and BITbit1 isn't used + * over-written, and if_is_24bit isn't used */ bios->fp.dual_link = bios->data[lvdsofs] & 1; - bios->fp.BITbit1 = bios->data[lvdsofs] & 2; - bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10; - break; - case 0x40: - bios->fp.dual_link = bios->data[lvdsofs] & 1; bios->fp.if_is_24bit = bios->data[lvdsofs] & 2; bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4]; bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10; 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 ((dev->pdev->device == 0x01d7) && + (dev->pdev->subsystem_vendor == 0x1028) && + (dev->pdev->subsystem_device == 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); @@ -3682,7 +3674,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent, uint16_t record, int record_len, int record_nr) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint32_t entry; uint16_t table; int i, v; @@ -3716,7 +3708,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, int *length) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint8_t *table; if (!bios->display.dp_table_ptr) { @@ -3725,7 +3717,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, } table = &bios->data[bios->display.dp_table_ptr]; - if (table[0] != 0x21) { + if (table[0] != 0x20 && table[0] != 0x21) { NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n", table[0]); return NULL; @@ -3765,7 +3757,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, */ struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint8_t *table = &bios->data[bios->display.script_table_ptr]; uint8_t *otable = NULL; uint16_t script; @@ -3918,8 +3910,8 @@ int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, i */ struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; - int cv = bios->pub.chip_version; + struct nvbios *bios = &dev_priv->vbios; + int cv = bios->chip_version; uint16_t clktable = 0, scriptptr; uint32_t sel_clk_binding, sel_clk; @@ -3978,8 +3970,8 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims */ struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; - int cv = bios->pub.chip_version, pllindex = 0; + struct nvbios *bios = &dev_priv->vbios; + int cv = bios->chip_version, pllindex = 0; uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0; uint32_t crystal_strap_mask, crystal_straps; @@ -4332,7 +4324,7 @@ static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint */ bios->major_version = bios->data[offset + 3]; - bios->pub.chip_version = bios->data[offset + 2]; + bios->chip_version = bios->data[offset + 2]; NV_TRACE(dev, "Bios version %02x.%02x.%02x.%02x\n", bios->data[offset + 3], bios->data[offset + 2], bios->data[offset + 1], bios->data[offset]); @@ -4402,7 +4394,7 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st } /* First entry is normal dac, 2nd tv-out perhaps? */ - bios->pub.dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff; + bios->dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff; return 0; } @@ -4526,8 +4518,8 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, st return -ENOSYS; } - bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]); - bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]); + bios->dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]); + bios->tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]); return 0; } @@ -4796,11 +4788,11 @@ 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->bdcb.dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX; - bios->bdcb.dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX; - bios->bdcb.dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX; - bios->bdcb.dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX; - bios->pub.digital_min_front_porch = 0x4b; + 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; bios->fp.duallink_transition_clk = 90000; @@ -4907,10 +4899,10 @@ 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]; - bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4]; - bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5]; - bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6]; - bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7]; + bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4]; + bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5]; + bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6]; + bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7]; if (bmplength > 74) { bios->fmaxvco = ROM32(bmp[67]); @@ -4984,7 +4976,8 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i else NV_WARN(dev, "DCB I2C table has more entries than indexable " - "(%d entries, max index 15)\n", i2ctable[2]); + "(%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() */ } @@ -5000,8 +4993,8 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i if (index == 0xf) return 0; - if (index > i2c_entries) { - NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n", + if (index >= i2c_entries) { + NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n", index, i2ctable[2]); return -ENOENT; } @@ -5036,7 +5029,7 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i static struct dcb_gpio_entry * new_gpio_entry(struct nvbios *bios) { - struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio; + struct dcb_gpio_table *gpio = &bios->dcb.gpio; return &gpio->entry[gpio->entries++]; } @@ -5045,14 +5038,14 @@ struct dcb_gpio_entry * nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; int i; - for (i = 0; i < bios->bdcb.gpio.entries; i++) { - if (bios->bdcb.gpio.entry[i].tag != tag) + for (i = 0; i < bios->dcb.gpio.entries; i++) { + if (bios->dcb.gpio.entry[i].tag != tag) continue; - return &bios->bdcb.gpio.entry[i]; + return &bios->dcb.gpio.entry[i]; } return NULL; @@ -5075,32 +5068,32 @@ parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset) gpio->tag = tag; gpio->line = line; gpio->invert = flags != 4; + gpio->entry = ent; } static void parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset) { + uint32_t entry = ROM32(bios->data[offset]); struct dcb_gpio_entry *gpio; - uint32_t ent = ROM32(bios->data[offset]); - uint8_t line = ent & 0x1f, - tag = ent >> 8 & 0xff; - if (tag == 0xff) + if ((entry & 0x0000ff00) == 0x0000ff00) return; gpio = new_gpio_entry(bios); - - /* Currently unused, we may need more fields parsed at some - * point. */ - gpio->tag = tag; - gpio->line = line; + gpio->tag = (entry & 0x0000ff00) >> 8; + gpio->line = (entry & 0x0000001f) >> 0; + gpio->state_default = (entry & 0x01000000) >> 24; + gpio->state[0] = (entry & 0x18000000) >> 27; + gpio->state[1] = (entry & 0x60000000) >> 29; + gpio->entry = entry; } static void parse_dcb_gpio_table(struct nvbios *bios) { struct drm_device *dev = bios->dev; - uint16_t gpio_table_ptr = bios->bdcb.gpio_table_ptr; + uint16_t gpio_table_ptr = bios->dcb.gpio_table_ptr; uint8_t *gpio_table = &bios->data[gpio_table_ptr]; int header_len = gpio_table[1], entries = gpio_table[2], @@ -5108,7 +5101,7 @@ parse_dcb_gpio_table(struct nvbios *bios) void (*parse_entry)(struct nvbios *, uint16_t) = NULL; int i; - if (bios->bdcb.version >= 0x40) { + if (bios->dcb.version >= 0x40) { if (gpio_table_ptr && entry_len != 4) { NV_WARN(dev, "Invalid DCB GPIO table entry length.\n"); return; @@ -5116,7 +5109,7 @@ parse_dcb_gpio_table(struct nvbios *bios) parse_entry = parse_dcb40_gpio_entry; - } else if (bios->bdcb.version >= 0x30) { + } else if (bios->dcb.version >= 0x30) { if (gpio_table_ptr && entry_len != 2) { NV_WARN(dev, "Invalid DCB GPIO table entry length.\n"); return; @@ -5124,7 +5117,7 @@ parse_dcb_gpio_table(struct nvbios *bios) parse_entry = parse_dcb30_gpio_entry; - } else if (bios->bdcb.version >= 0x22) { + } else if (bios->dcb.version >= 0x22) { /* * DCBs older than v3.0 don't really have a GPIO * table, instead they keep some GPIO info at fixed @@ -5158,30 +5151,82 @@ 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 nvbios *bios = &dev_priv->vbios; struct dcb_connector_table_entry *cte; - if (index >= bios->bdcb.connector.entries) + if (index >= bios->dcb.connector.entries) return NULL; - cte = &bios->bdcb.connector.entry[index]; + cte = &bios->dcb.connector.entry[index]; if (cte->type == 0xff) return NULL; return cte; } +static enum dcb_connector_type +divine_connector_type(struct nvbios *bios, int index) +{ + 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; + } else + if (encoders & (1 << OUTPUT_LVDS)) { + type = DCB_CONNECTOR_LVDS; + } else + if (encoders & (1 << OUTPUT_TV)) { + type = DCB_CONNECTOR_TV_0; + } + + return type; +} + +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; + + /* Gigabyte NX85T */ + if ((dev->pdev->device == 0x0421) && + (dev->pdev->subsystem_vendor == 0x1458) && + (dev->pdev->subsystem_device == 0x344c)) { + if (cte->type == DCB_CONNECTOR_HDMI_1) + cte->type = DCB_CONNECTOR_DVI_I; + } +} + static void parse_dcb_connector_table(struct nvbios *bios) { struct drm_device *dev = bios->dev; - struct dcb_connector_table *ct = &bios->bdcb.connector; + struct dcb_connector_table *ct = &bios->dcb.connector; struct dcb_connector_table_entry *cte; - uint8_t *conntab = &bios->data[bios->bdcb.connector_table_ptr]; + uint8_t *conntab = &bios->data[bios->dcb.connector_table_ptr]; uint8_t *entry; int i; - if (!bios->bdcb.connector_table_ptr) { + if (!bios->dcb.connector_table_ptr) { NV_DEBUG_KMS(dev, "No DCB connector table present\n"); return; } @@ -5199,12 +5244,14 @@ parse_dcb_connector_table(struct nvbios *bios) 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->index = (cte->entry & 0x00000f00) >> 8; + cte->index2 = (cte->entry & 0x00000f00) >> 8; switch (cte->entry & 0x00033000) { case 0x00001000: cte->gpio_tag = 0x07; @@ -5226,12 +5273,43 @@ parse_dcb_connector_table(struct nvbios *bios) 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_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); + } + } } -static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb) +static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb) { struct dcb_entry *entry = &dcb->entry[dcb->entries]; @@ -5241,7 +5319,7 @@ static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb) return entry; } -static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads) +static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads) { struct dcb_entry *entry = new_dcb_entry(dcb); @@ -5252,7 +5330,7 @@ static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads) /* "or" mostly unused in early gen crt modesetting, 0 is fine */ } -static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads) +static void fabricate_dvi_i_output(struct dcb_table *dcb, bool twoHeads) { struct dcb_entry *entry = new_dcb_entry(dcb); @@ -5279,7 +5357,7 @@ static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads) #endif } -static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads) +static void fabricate_tv_output(struct dcb_table *dcb, bool twoHeads) { struct dcb_entry *entry = new_dcb_entry(dcb); @@ -5290,13 +5368,13 @@ static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads) } static bool -parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, +parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, uint32_t conn, uint32_t conf, struct dcb_entry *entry) { entry->type = conn & 0xf; entry->i2c_index = (conn >> 4) & 0xf; entry->heads = (conn >> 8) & 0xf; - if (bdcb->version >= 0x40) + if (dcb->version >= 0x40) entry->connector = (conn >> 12) & 0xf; entry->bus = (conn >> 16) & 0xf; entry->location = (conn >> 20) & 0x3; @@ -5314,7 +5392,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, * Although the rest of a CRT conf dword is usually * zeros, mac biosen have stuff there so we must mask */ - entry->crtconf.maxfreq = (bdcb->version < 0x30) ? + entry->crtconf.maxfreq = (dcb->version < 0x30) ? (conf & 0xffff) * 10 : (conf & 0xff) * 10000; break; @@ -5323,7 +5401,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, uint32_t mask; if (conf & 0x1) entry->lvdsconf.use_straps_for_mode = true; - if (bdcb->version < 0x22) { + if (dcb->version < 0x22) { mask = ~0xd; /* * The laptop in bug 14567 lies and claims to not use @@ -5347,7 +5425,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, * Until we even try to use these on G8x, it's * useless reporting unknown bits. They all are. */ - if (bdcb->version >= 0x40) + if (dcb->version >= 0x40) break; NV_ERROR(dev, "Unknown LVDS configuration bits, " @@ -5357,7 +5435,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, } case OUTPUT_TV: { - if (bdcb->version >= 0x30) + if (dcb->version >= 0x30) entry->tvconf.has_component_output = conf & (0x8 << 4); else entry->tvconf.has_component_output = false; @@ -5384,8 +5462,10 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, break; case 0xe: /* weird g80 mobile type that "nv" treats as a terminator */ - bdcb->dcb.entries--; + dcb->entries--; return false; + default: + break; } /* unsure what DCB version introduces this, 3.0? */ @@ -5396,7 +5476,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, } static bool -parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb, +parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb, uint32_t conn, uint32_t conf, struct dcb_entry *entry) { switch (conn & 0x0000000f) { @@ -5462,27 +5542,27 @@ parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb, return true; } -static bool parse_dcb_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, +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(&bdcb->dcb); + struct dcb_entry *entry = new_dcb_entry(dcb); bool ret; - if (bdcb->version >= 0x20) - ret = parse_dcb20_entry(dev, bdcb, conn, conf, entry); + if (dcb->version >= 0x20) + ret = parse_dcb20_entry(dev, dcb, conn, conf, entry); else - ret = parse_dcb15_entry(dev, &bdcb->dcb, conn, conf, entry); + ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); if (!ret) return ret; - read_dcb_i2c_entry(dev, bdcb->version, bdcb->i2c_table, - entry->i2c_index, &bdcb->dcb.i2c[entry->i2c_index]); + 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 parsed_dcb *dcb) +void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb) { /* * DCB v2.0 lists each output combination separately. @@ -5534,8 +5614,7 @@ static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct bios_parsed_dcb *bdcb = &bios->bdcb; - struct parsed_dcb *dcb; + struct dcb_table *dcb = &bios->dcb; uint16_t dcbptr = 0, i2ctabptr = 0; uint8_t *dcbtable; uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES; @@ -5543,9 +5622,6 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) int recordlength = 8, confofs = 4; int i; - dcb = bios->pub.dcb = &bdcb->dcb; - dcb->entries = 0; - /* get the offset from 0x36 */ if (dev_priv->card_type > NV_04) { dcbptr = ROM16(bios->data[0x36]); @@ -5567,21 +5643,21 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) dcbtable = &bios->data[dcbptr]; /* get DCB version */ - bdcb->version = dcbtable[0]; + dcb->version = dcbtable[0]; NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n", - bdcb->version >> 4, bdcb->version & 0xf); + dcb->version >> 4, dcb->version & 0xf); - if (bdcb->version >= 0x20) { /* NV17+ */ + if (dcb->version >= 0x20) { /* NV17+ */ uint32_t sig; - if (bdcb->version >= 0x30) { /* NV40+ */ + if (dcb->version >= 0x30) { /* NV40+ */ headerlen = dcbtable[1]; entries = dcbtable[2]; recordlength = dcbtable[3]; i2ctabptr = ROM16(dcbtable[4]); sig = ROM32(dcbtable[6]); - bdcb->gpio_table_ptr = ROM16(dcbtable[10]); - bdcb->connector_table_ptr = ROM16(dcbtable[20]); + dcb->gpio_table_ptr = ROM16(dcbtable[10]); + dcb->connector_table_ptr = ROM16(dcbtable[20]); } else { i2ctabptr = ROM16(dcbtable[2]); sig = ROM32(dcbtable[4]); @@ -5593,7 +5669,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) "signature (%08X)\n", sig); return -EINVAL; } - } else if (bdcb->version >= 0x15) { /* some NV11 and NV20 */ + } else if (dcb->version >= 0x15) { /* some NV11 and NV20 */ char sig[8] = { 0 }; strncpy(sig, (char *)&dcbtable[-7], 7); @@ -5641,14 +5717,11 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) if (!i2ctabptr) NV_WARN(dev, "No pointer to DCB I2C port table\n"); else { - bdcb->i2c_table = &bios->data[i2ctabptr]; - if (bdcb->version >= 0x30) - bdcb->i2c_default_indices = bdcb->i2c_table[4]; + dcb->i2c_table = &bios->data[i2ctabptr]; + if (dcb->version >= 0x30) + dcb->i2c_default_indices = dcb->i2c_table[4]; } - parse_dcb_gpio_table(bios); - parse_dcb_connector_table(bios); - if (entries > DCB_MAX_NUM_ENTRIES) entries = DCB_MAX_NUM_ENTRIES; @@ -5673,7 +5746,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n", dcb->entries, connection, config); - if (!parse_dcb_entry(dev, bdcb, connection, config)) + if (!parse_dcb_entry(dev, dcb, connection, config)) break; } @@ -5681,18 +5754,22 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) * apart for v2.1+ not being known for requiring merging, this * guarantees dcbent->index is the index of the entry in the rom image */ - if (bdcb->version < 0x21) + if (dcb->version < 0x21) merge_like_dcb_entries(dev, dcb); - return dcb->entries ? 0 : -ENXIO; + 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 bios_parsed_dcb *bdcb = &bios->bdcb; - struct parsed_dcb *dcb = &bdcb->dcb; - int high = 0, i; + 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 @@ -5700,9 +5777,11 @@ fixup_legacy_connector(struct nvbios *bios) * 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 (bdcb->version >= 0x40) + 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 @@ -5710,37 +5789,38 @@ fixup_legacy_connector(struct nvbios *bios) * its own unique connector index. */ for (i = 0; i < dcb->entries; i++) { - if (dcb->entry[i].i2c_index == 0xf) - continue; - /* * 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].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; } - dcb->entry[i].connector = dcb->entry[i].i2c_index; - if (dcb->entry[i].connector > high) - high = dcb->entry[i].connector; + dcb->entry[i].connector = dcb->connector.entries++; + if (i2c != 0xf) + i2c_conn[i2c] = dcb->connector.entries; } - for (i = 0; i < dcb->entries; i++) { - if (dcb->entry[i].i2c_index != 0xf) - continue; - - dcb->entry[i].connector = ++high; + /* 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 parsed_dcb *dcb = &bios->bdcb.dcb; + struct dcb_table *dcb = &bios->dcb; int i; for (i = 0; i < dcb->entries; i++) { @@ -5826,7 +5906,7 @@ static int load_nv17_hw_sequencer_ucode(struct drm_device *dev, uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; const uint8_t edid_sig[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; uint16_t offset = 0; @@ -5859,7 +5939,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, struct dcb_entry *dcbent) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; struct init_exec iexec = { true, false }; mutex_lock(&bios->lock); @@ -5872,7 +5952,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, static bool NVInitVBIOS(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; memset(bios, 0, sizeof(struct nvbios)); mutex_init(&bios->lock); @@ -5888,7 +5968,7 @@ static bool NVInitVBIOS(struct drm_device *dev) static int nouveau_parse_vbios_struct(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' }; const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 }; int offset; @@ -5915,7 +5995,7 @@ int nouveau_run_vbios_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; int i, ret = 0; NVLockVgaCrtcs(dev, false); @@ -5946,9 +6026,9 @@ nouveau_run_vbios_init(struct drm_device *dev) } if (dev_priv->card_type >= NV_50) { - for (i = 0; i < bios->bdcb.dcb.entries; i++) { + for (i = 0; i < bios->dcb.entries; i++) { nouveau_bios_run_display_table(dev, - &bios->bdcb.dcb.entry[i], + &bios->dcb.entry[i], 0, 0); } } @@ -5962,11 +6042,11 @@ 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 nvbios *bios = &dev_priv->vbios; struct dcb_i2c_entry *entry; int i; - entry = &bios->bdcb.dcb.i2c[0]; + entry = &bios->dcb.i2c[0]; for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++) nouveau_i2c_fini(dev, entry); } @@ -5975,13 +6055,11 @@ int nouveau_bios_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint32_t saved_nv_pextdev_boot_0; bool was_locked; int ret; - dev_priv->vbios = &bios->pub; - if (!NVInitVBIOS(dev)) return -ENODEV; @@ -6023,10 +6101,8 @@ nouveau_bios_init(struct drm_device *dev) bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0); ret = nouveau_run_vbios_init(dev); - if (ret) { - dev_priv->vbios = NULL; + if (ret) return ret; - } /* feature_byte on BMP is poor, but init always sets CR4B */ was_locked = NVLockVgaCrtcs(dev, false); diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index fd94bd6dc264..c0d7b0a3ece0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -34,9 +34,71 @@ #define DCB_LOC_ON_CHIP 0 +struct dcb_i2c_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]; +}; + +enum dcb_connector_type { + DCB_CONNECTOR_VGA = 0x00, + DCB_CONNECTOR_TV_0 = 0x10, + DCB_CONNECTOR_TV_1 = 0x11, + DCB_CONNECTOR_TV_3 = 0x13, + DCB_CONNECTOR_DVI_I = 0x30, + DCB_CONNECTOR_DVI_D = 0x31, + DCB_CONNECTOR_LVDS = 0x40, + DCB_CONNECTOR_DP = 0x46, + DCB_CONNECTOR_eDP = 0x47, + DCB_CONNECTOR_HDMI_0 = 0x60, + DCB_CONNECTOR_HDMI_1 = 0x61, + 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; +}; + +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, + OUTPUT_TMDS = 2, + OUTPUT_LVDS = 3, + OUTPUT_DP = 6, + OUTPUT_ANY = -1 +}; + struct dcb_entry { int index; /* may not be raw dcb index if merging has happened */ - uint8_t type; + enum dcb_type type; uint8_t i2c_index; uint8_t heads; uint8_t connector; @@ -71,69 +133,22 @@ struct dcb_entry { bool i2c_upper_default; }; -struct dcb_i2c_entry { - uint8_t port_type; - uint8_t read, write; - struct nouveau_i2c_chan *chan; -}; +struct dcb_table { + uint8_t version; -struct parsed_dcb { int entries; struct dcb_entry entry[DCB_MAX_NUM_ENTRIES]; - struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES]; -}; - -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; -}; - -struct parsed_dcb_gpio { - int entries; - struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES]; -}; - -struct dcb_connector_table_entry { - uint32_t entry; - uint8_t type; - uint8_t index; - uint8_t gpio_tag; -}; - -struct dcb_connector_table { - int entries; - struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES]; -}; - -struct bios_parsed_dcb { - uint8_t version; - - struct parsed_dcb dcb; 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 parsed_dcb_gpio gpio; + struct dcb_gpio_table gpio; uint16_t connector_table_ptr; struct dcb_connector_table connector; }; -enum nouveau_encoder_type { - OUTPUT_ANALOG = 0, - OUTPUT_TV = 1, - OUTPUT_TMDS = 2, - OUTPUT_LVDS = 3, - OUTPUT_DP = 6, - OUTPUT_ANY = -1 -}; - enum nouveau_or { OUTPUT_A = (1 << 0), OUTPUT_B = (1 << 1), @@ -190,8 +205,8 @@ struct pll_lims { int refclk; }; -struct nouveau_bios_info { - struct parsed_dcb *dcb; +struct nvbios { + struct drm_device *dev; uint8_t chip_version; @@ -199,11 +214,6 @@ struct nouveau_bios_info { uint32_t tvdactestval; uint8_t digital_min_front_porch; bool fp_no_ddc; -}; - -struct nvbios { - struct drm_device *dev; - struct nouveau_bios_info pub; struct mutex lock; @@ -234,7 +244,7 @@ struct nvbios { uint16_t some_script_ptr; /* BIT I + 14 */ uint16_t init96_tbl_ptr; /* BIT I + 16 */ - struct bios_parsed_dcb bdcb; + struct dcb_table dcb; struct { int crtchead; @@ -260,7 +270,6 @@ struct nvbios { bool reset_after_pclk_change; bool dual_link; bool link_c_increment; - bool BITbit1; bool if_is_24bit; int duallink_transition_clk; uint8_t strapless_is_24bit; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 028719fddf76..957d17629840 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -34,6 +34,7 @@ #include "nouveau_dma.h" #include <linux/log2.h> +#include <linux/slab.h> static void nouveau_bo_del_ttm(struct ttm_buffer_object *bo) @@ -71,7 +72,7 @@ nouveau_bo_fixup_align(struct drm_device *dev, * many small buffers. */ if (dev_priv->card_type == NV_50) { - uint32_t block_size = nouveau_mem_fb_amount(dev) >> 15; + uint32_t block_size = dev_priv->vram_size >> 15; int i; switch (tile_flags) { @@ -153,7 +154,7 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, nvbo->placement.fpfn = 0; nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0; - nouveau_bo_placement_set(nvbo, flags); + nouveau_bo_placement_set(nvbo, flags, 0); nvbo->channel = chan; ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size, @@ -172,26 +173,33 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, return 0; } +static void +set_placement_list(uint32_t *pl, unsigned *n, uint32_t type, uint32_t flags) +{ + *n = 0; + + if (type & TTM_PL_FLAG_VRAM) + pl[(*n)++] = TTM_PL_FLAG_VRAM | flags; + if (type & TTM_PL_FLAG_TT) + pl[(*n)++] = TTM_PL_FLAG_TT | flags; + if (type & TTM_PL_FLAG_SYSTEM) + pl[(*n)++] = TTM_PL_FLAG_SYSTEM | flags; +} + void -nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t memtype) +nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t type, uint32_t busy) { - int n = 0; - - if (memtype & TTM_PL_FLAG_VRAM) - nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING; - if (memtype & TTM_PL_FLAG_TT) - nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; - if (memtype & TTM_PL_FLAG_SYSTEM) - nvbo->placements[n++] = TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING; - nvbo->placement.placement = nvbo->placements; - nvbo->placement.busy_placement = nvbo->placements; - nvbo->placement.num_placement = n; - nvbo->placement.num_busy_placement = n; - - if (nvbo->pin_refcnt) { - while (n--) - nvbo->placements[n] |= TTM_PL_FLAG_NO_EVICT; - } + struct ttm_placement *pl = &nvbo->placement; + uint32_t flags = TTM_PL_MASK_CACHING | + (nvbo->pin_refcnt ? TTM_PL_FLAG_NO_EVICT : 0); + + pl->placement = nvbo->placements; + set_placement_list(nvbo->placements, &pl->num_placement, + type, flags); + + pl->busy_placement = nvbo->busy_placements; + set_placement_list(nvbo->busy_placements, &pl->num_busy_placement, + type | busy, flags); } int @@ -199,7 +207,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) { struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev); struct ttm_buffer_object *bo = &nvbo->bo; - int ret, i; + int ret; if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) { NV_ERROR(nouveau_bdev(bo->bdev)->dev, @@ -215,9 +223,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) if (ret) goto out; - nouveau_bo_placement_set(nvbo, memtype); - for (i = 0; i < nvbo->placement.num_placement; i++) - nvbo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + nouveau_bo_placement_set(nvbo, memtype, 0); ret = ttm_bo_validate(bo, &nvbo->placement, false, false); if (ret == 0) { @@ -244,7 +250,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) { struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev); struct ttm_buffer_object *bo = &nvbo->bo; - int ret, i; + int ret; if (--nvbo->pin_refcnt) return 0; @@ -253,8 +259,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) if (ret) return ret; - for (i = 0; i < nvbo->placement.num_placement; i++) - nvbo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + nouveau_bo_placement_set(nvbo, bo->mem.placement, 0); ret = ttm_bo_validate(bo, &nvbo->placement, false, false); if (ret == 0) { @@ -395,8 +400,8 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->io_addr = NULL; man->io_offset = drm_get_resource_start(dev, 1); man->io_size = drm_get_resource_len(dev, 1); - if (man->io_size > nouveau_mem_fb_amount(dev)) - man->io_size = nouveau_mem_fb_amount(dev); + if (man->io_size > dev_priv->vram_size) + man->io_size = dev_priv->vram_size; man->gpu_offset = dev_priv->vm_vram_base; break; @@ -439,11 +444,11 @@ nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) switch (bo->mem.mem_type) { case TTM_PL_VRAM: - nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT | + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT, TTM_PL_FLAG_SYSTEM); break; default: - nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM); + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM, 0); break; } diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c index ee2b84504d05..88f9bc0941eb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_calc.c +++ b/drivers/gpu/drm/nouveau/nouveau_calc.c @@ -274,7 +274,7 @@ getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk, * returns calculated clock */ struct drm_nouveau_private *dev_priv = dev->dev_private; - int cv = dev_priv->vbios->chip_version; + int cv = dev_priv->vbios.chip_version; int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq; int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m; int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n; @@ -373,7 +373,7 @@ getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk, * returns calculated clock */ struct drm_nouveau_private *dev_priv = dev->dev_private; - int chip_version = dev_priv->vbios->chip_version; + int chip_version = dev_priv->vbios.chip_version; int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq; int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq; int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq; diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index 2281f99da7fc..1fc57ef58295 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -35,22 +35,27 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_bo *pb = chan->pushbuf_bo; struct nouveau_gpuobj *pushbuf = NULL; - uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT; int ret; + if (dev_priv->card_type >= NV_50) { + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, + dev_priv->vm_end, NV_DMA_ACCESS_RO, + NV_DMA_TARGET_AGP, &pushbuf); + chan->pushbuf_base = pb->bo.offset; + } else if (pb->bo.mem.mem_type == TTM_PL_TT) { ret = nouveau_gpuobj_gart_dma_new(chan, 0, dev_priv->gart_info.aper_size, NV_DMA_ACCESS_RO, &pushbuf, NULL); - chan->pushbuf_base = start; + chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT; } else if (dev_priv->card_type != NV_04) { ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, dev_priv->fb_available_size, NV_DMA_ACCESS_RO, NV_DMA_TARGET_VIDMEM, &pushbuf); - chan->pushbuf_base = start; + chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT; } else { /* NV04 cmdbuf hack, from original ddx.. not sure of it's * exact reason for existing :) PCI access to cmdbuf in @@ -61,7 +66,7 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan) dev_priv->fb_available_size, NV_DMA_ACCESS_RO, NV_DMA_TARGET_PCI, &pushbuf); - chan->pushbuf_base = start; + chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT; } ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf); @@ -137,7 +142,6 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, GFP_KERNEL); if (!dev_priv->fifos[channel]) return -ENOMEM; - dev_priv->fifo_alloc_count++; chan = dev_priv->fifos[channel]; INIT_LIST_HEAD(&chan->nvsw.vbl_wait); INIT_LIST_HEAD(&chan->fence.pending); @@ -275,9 +279,18 @@ nouveau_channel_free(struct nouveau_channel *chan) */ nouveau_fence_fini(chan); - /* Ensure the channel is no longer active on the GPU */ + /* This will prevent pfifo from switching channels. */ pfifo->reassign(dev, false); + /* We want to give pgraph a chance to idle and get rid of all potential + * errors. We need to do this before the lock, otherwise the irq handler + * is unable to process them. + */ + if (pgraph->channel(dev) == chan) + nouveau_wait_for_idle(dev); + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + pgraph->fifo_access(dev, false); if (pgraph->channel(dev) == chan) pgraph->unload_context(dev); @@ -293,6 +306,8 @@ nouveau_channel_free(struct nouveau_channel *chan) pfifo->reassign(dev, true); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + /* Release the channel's resources */ nouveau_gpuobj_ref_del(dev, &chan->pushbuf); if (chan->pushbuf_bo) { @@ -305,7 +320,6 @@ nouveau_channel_free(struct nouveau_channel *chan) iounmap(chan->user); dev_priv->fifos[chan->id] = NULL; - dev_priv->fifo_alloc_count--; kfree(chan); } @@ -369,6 +383,14 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, return ret; init->channel = chan->id; + if (chan->dma.ib_max) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | + NOUVEAU_GEM_DOMAIN_GART; + else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; + else + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; + init->subchan[0].handle = NvM2MF; if (dev_priv->card_type < NV_50) init->subchan[0].grclass = 0x0039; @@ -408,7 +430,6 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data, ***********************************/ struct drm_ioctl_desc nouveau_ioctls[] = { - DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH), @@ -418,13 +439,9 @@ struct drm_ioctl_desc nouveau_ioctls[] = { DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH), - DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH), - DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH), - DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH), - DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH), }; int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls); diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index d2f63353ea97..14afe1e47e57 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -218,7 +218,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector, connector->interlace_allowed = true; } - if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) { + if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { drm_connector_property_set_value(connector, dev->mode_config.dvi_i_subconnector_property, nv_encoder->dcb->type == OUTPUT_TMDS ? @@ -236,15 +236,17 @@ nouveau_connector_detect(struct drm_connector *connector) struct nouveau_i2c_chan *i2c; int type, flags; - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS) nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); if (nv_encoder && nv_connector->native_mode) { + unsigned status = connector_status_connected; + #ifdef CONFIG_ACPI if (!nouveau_ignorelid && !acpi_lid_open()) - return connector_status_disconnected; + status = connector_status_unknown; #endif nouveau_connector_set_encoder(connector, nv_encoder); - return connector_status_connected; + return status; } /* Cleanup the previous EDID block. */ @@ -279,7 +281,7 @@ nouveau_connector_detect(struct drm_connector *connector) * same i2c channel so the value returned from ddc_detect * isn't necessarily correct. */ - if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) { + if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL) type = OUTPUT_TMDS; else @@ -300,7 +302,7 @@ nouveau_connector_detect(struct drm_connector *connector) detect_analog: nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG); - if (!nv_encoder) + if (!nv_encoder && !nouveau_tv_disable) nv_encoder = find_encoder_by_type(connector, OUTPUT_TV); if (nv_encoder) { struct drm_encoder *encoder = to_drm_encoder(nv_encoder); @@ -321,11 +323,11 @@ detect_analog: static void nouveau_connector_force(struct drm_connector *connector) { - struct drm_device *dev = connector->dev; + struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder; int type; - if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) { + if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { if (connector->force == DRM_FORCE_ON_DIGITAL) type = OUTPUT_TMDS; else @@ -335,7 +337,7 @@ nouveau_connector_force(struct drm_connector *connector) nv_encoder = find_encoder_by_type(connector, type); if (!nv_encoder) { - NV_ERROR(dev, "can't find encoder to force %s on!\n", + NV_ERROR(connector->dev, "can't find encoder to force %s on!\n", drm_get_connector_name(connector)); connector->status = connector_status_disconnected; return; @@ -369,7 +371,7 @@ nouveau_connector_set_property(struct drm_connector *connector, } /* LVDS always needs gpu scaling */ - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && + if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS && value == DRM_MODE_SCALE_NONE) return -EINVAL; @@ -535,7 +537,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) /* If we're not LVDS, destroy the previous native mode, the attached * monitor could have changed. */ - if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && + if (nv_connector->dcb->type != DCB_CONNECTOR_LVDS && nv_connector->native_mode) { drm_mode_destroy(dev, nv_connector->native_mode); nv_connector->native_mode = NULL; @@ -563,7 +565,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) ret = get_slave_funcs(nv_encoder)-> get_modes(to_drm_encoder(nv_encoder), connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + if (nv_encoder->dcb->type == OUTPUT_LVDS) ret += nouveau_connector_scaler_modes_add(connector); return ret; @@ -613,6 +615,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector, clock *= 3; break; + default: + BUG_ON(1); + return MODE_BAD; } if (clock < min_clock) @@ -680,7 +685,7 @@ nouveau_connector_create_lvds(struct drm_device *dev, /* Firstly try getting EDID over DDC, if allowed and I2C channel * is available. */ - if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf) + if (!dev_priv->vbios.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf) i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); if (i2c) { @@ -695,7 +700,7 @@ nouveau_connector_create_lvds(struct drm_device *dev, */ if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) && (nv_encoder->dcb->lvdsconf.use_straps_for_mode || - dev_priv->VBIOS.pub.fp_no_ddc)) { + dev_priv->vbios.fp_no_ddc)) { nv_connector->native_mode = drm_mode_duplicate(dev, &native); goto out; } @@ -704,7 +709,7 @@ nouveau_connector_create_lvds(struct drm_device *dev, * stored for the panel stored in them. */ if (!nv_connector->edid && !nv_connector->native_mode && - !dev_priv->VBIOS.pub.fp_no_ddc) { + !dev_priv->vbios.fp_no_ddc) { struct edid *edid = (struct edid *)nouveau_bios_embedded_edid(dev); if (edid) { @@ -739,46 +744,66 @@ out: } int -nouveau_connector_create(struct drm_device *dev, int index, int type) +nouveau_connector_create(struct drm_device *dev, + struct dcb_connector_table_entry *dcb) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_connector *nv_connector = NULL; struct drm_connector *connector; struct drm_encoder *encoder; - int ret; + int ret, type; NV_DEBUG_KMS(dev, "\n"); - nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); - if (!nv_connector) - return -ENOMEM; - nv_connector->dcb = nouveau_bios_connector_entry(dev, index); - connector = &nv_connector->base; - - switch (type) { - case DRM_MODE_CONNECTOR_VGA: + switch (dcb->type) { + case DCB_CONNECTOR_NONE: + return 0; + case DCB_CONNECTOR_VGA: NV_INFO(dev, "Detected a VGA connector\n"); + type = DRM_MODE_CONNECTOR_VGA; break; - case DRM_MODE_CONNECTOR_DVID: - NV_INFO(dev, "Detected a DVI-D connector\n"); + case DCB_CONNECTOR_TV_0: + case DCB_CONNECTOR_TV_1: + case DCB_CONNECTOR_TV_3: + NV_INFO(dev, "Detected a TV connector\n"); + type = DRM_MODE_CONNECTOR_TV; break; - case DRM_MODE_CONNECTOR_DVII: + case DCB_CONNECTOR_DVI_I: NV_INFO(dev, "Detected a DVI-I connector\n"); + type = DRM_MODE_CONNECTOR_DVII; break; - case DRM_MODE_CONNECTOR_LVDS: - NV_INFO(dev, "Detected a LVDS connector\n"); + case DCB_CONNECTOR_DVI_D: + NV_INFO(dev, "Detected a DVI-D connector\n"); + type = DRM_MODE_CONNECTOR_DVID; break; - case DRM_MODE_CONNECTOR_TV: - NV_INFO(dev, "Detected a TV connector\n"); + case DCB_CONNECTOR_HDMI_0: + case DCB_CONNECTOR_HDMI_1: + NV_INFO(dev, "Detected a HDMI connector\n"); + type = DRM_MODE_CONNECTOR_HDMIA; + break; + case DCB_CONNECTOR_LVDS: + NV_INFO(dev, "Detected a LVDS connector\n"); + type = DRM_MODE_CONNECTOR_LVDS; break; - case DRM_MODE_CONNECTOR_DisplayPort: + case DCB_CONNECTOR_DP: NV_INFO(dev, "Detected a DisplayPort connector\n"); + type = DRM_MODE_CONNECTOR_DisplayPort; break; - default: - NV_ERROR(dev, "Unknown connector, this is not good.\n"); + case DCB_CONNECTOR_eDP: + NV_INFO(dev, "Detected an eDP connector\n"); + type = DRM_MODE_CONNECTOR_eDP; break; + default: + NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type); + return -EINVAL; } + nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); + if (!nv_connector) + return -ENOMEM; + nv_connector->dcb = dcb; + connector = &nv_connector->base; + /* defaults, will get overridden in detect() */ connector->interlace_allowed = false; connector->doublescan_allowed = false; @@ -786,55 +811,65 @@ nouveau_connector_create(struct drm_device *dev, int index, int type) drm_connector_init(dev, connector, &nouveau_connector_funcs, type); drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); + /* attach encoders */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + if (nv_encoder->dcb->connector != dcb->index) + continue; + + if (get_slave_funcs(nv_encoder)) + get_slave_funcs(nv_encoder)->create_resources(encoder, connector); + + drm_mode_connector_attach_encoder(connector, encoder); + } + + if (!connector->encoder_ids[0]) { + NV_WARN(dev, " no encoders, ignoring\n"); + drm_connector_cleanup(connector); + kfree(connector); + return 0; + } + /* Init DVI-I specific properties */ - if (type == DRM_MODE_CONNECTOR_DVII) { + if (dcb->type == DCB_CONNECTOR_DVI_I) { drm_mode_create_dvi_i_properties(dev); 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); } - if (type != DRM_MODE_CONNECTOR_LVDS) + if (dcb->type != DCB_CONNECTOR_LVDS) nv_connector->use_dithering = false; - if (type == DRM_MODE_CONNECTOR_DVID || - type == DRM_MODE_CONNECTOR_DVII || - type == DRM_MODE_CONNECTOR_LVDS || - type == DRM_MODE_CONNECTOR_DisplayPort) { - nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; - - 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); - - } else { - nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; - - if (type == DRM_MODE_CONNECTOR_VGA && - dev_priv->card_type >= NV_50) { + switch (dcb->type) { + case DCB_CONNECTOR_VGA: + if (dev_priv->card_type >= NV_50) { drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, nv_connector->scaling_mode); } - } - - /* attach encoders */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - - if (nv_encoder->dcb->connector != index) - continue; - - if (get_slave_funcs(nv_encoder)) - get_slave_funcs(nv_encoder)->create_resources(encoder, connector); + /* fall-through */ + case DCB_CONNECTOR_TV_0: + case DCB_CONNECTOR_TV_1: + case DCB_CONNECTOR_TV_3: + nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; + break; + default: + nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; - drm_mode_connector_attach_encoder(connector, encoder); + 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); + break; } drm_sysfs_connector_add(connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { + if (dcb->type == DCB_CONNECTOR_LVDS) { ret = nouveau_connector_create_lvds(dev, connector); if (ret) { connector->funcs->destroy(connector); diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 728b8090e5ff..4ef38abc2d9c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -49,6 +49,7 @@ static inline struct nouveau_connector *nouveau_connector( return container_of(con, struct nouveau_connector, base); } -int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type); +int nouveau_connector_create(struct drm_device *, + struct dcb_connector_table_entry *); #endif /* __NOUVEAU_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index d79db3698f16..a251886a0ce6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -47,12 +47,23 @@ nouveau_debugfs_channel_info(struct seq_file *m, void *data) seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2); seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2); seq_printf(m, " free: 0x%08x\n", chan->dma.free << 2); + if (chan->dma.ib_max) { + seq_printf(m, " ib max: 0x%08x\n", chan->dma.ib_max); + seq_printf(m, " ib put: 0x%08x\n", chan->dma.ib_put); + seq_printf(m, " ib free: 0x%08x\n", chan->dma.ib_free); + } seq_printf(m, "gpu fifo state:\n"); seq_printf(m, " get: 0x%08x\n", nvchan_rd32(chan, chan->user_get)); seq_printf(m, " put: 0x%08x\n", nvchan_rd32(chan, chan->user_put)); + if (chan->dma.ib_max) { + seq_printf(m, " ib get: 0x%08x\n", + nvchan_rd32(chan, 0x88)); + seq_printf(m, " ib put: 0x%08x\n", + nvchan_rd32(chan, 0x8c)); + } seq_printf(m, "last fence : %d\n", chan->fence.sequence); seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack); @@ -126,16 +137,28 @@ nouveau_debugfs_memory_info(struct seq_file *m, void *data) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_minor *minor = node->minor; - struct drm_device *dev = minor->dev; + struct drm_nouveau_private *dev_priv = minor->dev->dev_private; + + seq_printf(m, "VRAM total: %dKiB\n", (int)(dev_priv->vram_size >> 10)); + return 0; +} + +static int +nouveau_debugfs_vbios_image(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_nouveau_private *dev_priv = node->minor->dev->dev_private; + int i; - seq_printf(m, "VRAM total: %dKiB\n", - (int)(nouveau_mem_fb_amount(dev) >> 10)); + for (i = 0; i < dev_priv->vbios.length; i++) + seq_printf(m, "%c", dev_priv->vbios.data[i]); return 0; } static struct drm_info_list nouveau_debugfs_list[] = { { "chipset", nouveau_debugfs_chipset_info, 0, NULL }, { "memory", nouveau_debugfs_memory_info, 0, NULL }, + { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL }, }; #define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index dfc94391d71e..cf1c5c0a0abe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -39,11 +39,8 @@ nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) if (drm_fb->fbdev) nouveau_fbcon_remove(dev, drm_fb); - if (fb->nvbo) { - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(fb->nvbo->gem); - mutex_unlock(&dev->struct_mutex); - } + if (fb->nvbo) + drm_gem_object_unreference_unlocked(fb->nvbo->gem); drm_framebuffer_cleanup(drm_fb); kfree(fb); diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c index 50d9e67745af..65c441a1999f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.c +++ b/drivers/gpu/drm/nouveau/nouveau_dma.c @@ -32,7 +32,22 @@ void nouveau_dma_pre_init(struct nouveau_channel *chan) { - chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2; + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + struct nouveau_bo *pushbuf = chan->pushbuf_bo; + + if (dev_priv->card_type == NV_50) { + const int ib_size = pushbuf->bo.mem.size / 2; + + chan->dma.ib_base = (pushbuf->bo.mem.size - ib_size) >> 2; + chan->dma.ib_max = (ib_size / 8) - 1; + chan->dma.ib_put = 0; + chan->dma.ib_free = chan->dma.ib_max - chan->dma.ib_put; + + chan->dma.max = (pushbuf->bo.mem.size - ib_size) >> 2; + } else { + chan->dma.max = (pushbuf->bo.mem.size >> 2) - 2; + } + chan->dma.put = 0; chan->dma.cur = chan->dma.put; chan->dma.free = chan->dma.max - chan->dma.cur; @@ -162,12 +177,106 @@ READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout) return (val - chan->pushbuf_base) >> 2; } +void +nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo, + int delta, int length) +{ + struct nouveau_bo *pb = chan->pushbuf_bo; + uint64_t offset = bo->bo.offset + delta; + int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base; + + BUG_ON(chan->dma.ib_free < 1); + nouveau_bo_wr32(pb, ip++, lower_32_bits(offset)); + nouveau_bo_wr32(pb, ip++, upper_32_bits(offset) | length << 8); + + chan->dma.ib_put = (chan->dma.ib_put + 1) & chan->dma.ib_max; + + DRM_MEMORYBARRIER(); + /* Flush writes. */ + nouveau_bo_rd32(pb, 0); + + nvchan_wr32(chan, 0x8c, chan->dma.ib_put); + chan->dma.ib_free--; +} + +static int +nv50_dma_push_wait(struct nouveau_channel *chan, int count) +{ + uint32_t cnt = 0, prev_get = 0; + + while (chan->dma.ib_free < count) { + uint32_t get = nvchan_rd32(chan, 0x88); + if (get != prev_get) { + prev_get = get; + cnt = 0; + } + + if ((++cnt & 0xff) == 0) { + DRM_UDELAY(1); + if (cnt > 100000) + return -EBUSY; + } + + chan->dma.ib_free = get - chan->dma.ib_put; + if (chan->dma.ib_free <= 0) + chan->dma.ib_free += chan->dma.ib_max + 1; + } + + return 0; +} + +static int +nv50_dma_wait(struct nouveau_channel *chan, int slots, int count) +{ + uint32_t cnt = 0, prev_get = 0; + int ret; + + ret = nv50_dma_push_wait(chan, slots + 1); + if (unlikely(ret)) + return ret; + + while (chan->dma.free < count) { + int get = READ_GET(chan, &prev_get, &cnt); + if (unlikely(get < 0)) { + if (get == -EINVAL) + continue; + + return get; + } + + if (get <= chan->dma.cur) { + chan->dma.free = chan->dma.max - chan->dma.cur; + if (chan->dma.free >= count) + break; + + FIRE_RING(chan); + do { + get = READ_GET(chan, &prev_get, &cnt); + if (unlikely(get < 0)) { + if (get == -EINVAL) + continue; + return get; + } + } while (get == 0); + chan->dma.cur = 0; + chan->dma.put = 0; + } + + chan->dma.free = get - chan->dma.cur - 1; + } + + return 0; +} + int -nouveau_dma_wait(struct nouveau_channel *chan, int size) +nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size) { uint32_t prev_get = 0, cnt = 0; int get; + if (chan->dma.ib_max) + return nv50_dma_wait(chan, slots, size); + while (chan->dma.free < size) { get = READ_GET(chan, &prev_get, &cnt); if (unlikely(get == -EBUSY)) diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h index dabfd655f93e..8b05c15866d5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.h +++ b/drivers/gpu/drm/nouveau/nouveau_dma.h @@ -31,6 +31,9 @@ #define NOUVEAU_DMA_DEBUG 0 #endif +void nv50_dma_push(struct nouveau_channel *, struct nouveau_bo *, + int delta, int length); + /* * There's a hw race condition where you can't jump to your PUT offset, * to avoid this we jump to offset + SKIPS and fill the difference with @@ -96,13 +99,11 @@ enum { static __must_check inline int RING_SPACE(struct nouveau_channel *chan, int size) { - if (chan->dma.free < size) { - int ret; + int ret; - ret = nouveau_dma_wait(chan, size); - if (ret) - return ret; - } + ret = nouveau_dma_wait(chan, 1, size); + if (ret) + return ret; chan->dma.free -= size; return 0; @@ -146,7 +147,13 @@ FIRE_RING(struct nouveau_channel *chan) return; chan->accel_done = true; - WRITE_PUT(chan->dma.cur); + if (chan->dma.ib_max) { + nv50_dma_push(chan, chan->pushbuf_bo, chan->dma.put << 2, + (chan->dma.cur - chan->dma.put) << 2); + } else { + WRITE_PUT(chan->dma.cur); + } + chan->dma.put = chan->dma.cur; } diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index f954ad93e81f..deeb21c6865c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -483,7 +483,7 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT); ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT); - for (;;) { + for (i = 0; i < 16; i++) { nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000); nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl); nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000); @@ -502,6 +502,12 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, break; } + if (i == 16) { + NV_ERROR(dev, "auxch DEFER too many times, bailing\n"); + ret = -EREMOTEIO; + goto out; + } + if (cmd & 1) { if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) { ret = -EREMOTEIO; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index da3b93b84502..1de974acbc65 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -75,14 +75,22 @@ MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status"); int nouveau_ignorelid = 0; module_param_named(ignorelid, nouveau_ignorelid, int, 0400); -MODULE_PARM_DESC(noagp, "Disable all acceleration"); +MODULE_PARM_DESC(noaccel, "Disable all acceleration"); int nouveau_noaccel = 0; module_param_named(noaccel, nouveau_noaccel, int, 0400); -MODULE_PARM_DESC(noagp, "Disable fbcon acceleration"); +MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); int nouveau_nofbaccel = 0; module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400); +MODULE_PARM_DESC(override_conntype, "Ignore DCB connector type"); +int nouveau_override_conntype = 0; +module_param_named(override_conntype, nouveau_override_conntype, int, 0400); + +MODULE_PARM_DESC(tv_disable, "Disable TV-out detection\n"); +int nouveau_tv_disable = 0; +module_param_named(tv_disable, nouveau_tv_disable, int, 0400); + MODULE_PARM_DESC(tv_norm, "Default TV norm.\n" "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n" "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n" @@ -135,7 +143,7 @@ nouveau_pci_remove(struct pci_dev *pdev) drm_put_dev(dev); } -static int +int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) { struct drm_device *dev = pci_get_drvdata(pdev); @@ -154,9 +162,11 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) if (pm_state.event == PM_EVENT_PRETHAW) return 0; + NV_INFO(dev, "Disabling fbcon acceleration...\n"); fbdev_flags = dev_priv->fbdev_info->flags; dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED; + NV_INFO(dev, "Unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -233,7 +243,7 @@ out_abort: return ret; } -static int +int nouveau_pci_resume(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); @@ -402,8 +412,10 @@ static int __init nouveau_init(void) nouveau_modeset = 1; } - if (nouveau_modeset == 1) + if (nouveau_modeset == 1) { driver.driver_features |= DRIVER_MODESET; + nouveau_register_dsm_handler(); + } return drm_init(&driver); } @@ -411,6 +423,7 @@ static int __init nouveau_init(void) static void __exit nouveau_exit(void) { drm_exit(&driver); + nouveau_unregister_dsm_handler(); } module_init(nouveau_init); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 1c15ef37b71c..ace630aa89e1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -34,7 +34,7 @@ #define DRIVER_MAJOR 0 #define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 15 +#define DRIVER_PATCHLEVEL 16 #define NOUVEAU_FAMILY 0x0000FFFF #define NOUVEAU_FLAGS 0xFFFF0000 @@ -76,6 +76,7 @@ struct nouveau_bo { struct ttm_buffer_object bo; struct ttm_placement placement; u32 placements[3]; + u32 busy_placements[3]; struct ttm_bo_kmap_obj kmap; struct list_head head; @@ -83,6 +84,7 @@ struct nouveau_bo { struct drm_file *reserved_by; struct list_head entry; int pbbo_index; + bool validate_mapped; struct nouveau_channel *channel; @@ -239,6 +241,11 @@ struct nouveau_channel { int cur; int put; /* access via pushbuf_bo */ + + int ib_base; + int ib_max; + int ib_free; + int ib_put; } dma; uint32_t sw_subchannel[8]; @@ -513,6 +520,7 @@ struct drm_nouveau_private { struct workqueue_struct *wq; struct work_struct irq_work; + struct work_struct hpd_work; struct list_head vbl_waiting; @@ -527,12 +535,14 @@ struct drm_nouveau_private { struct fb_info *fbdev_info; - int fifo_alloc_count; struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR]; struct nouveau_engine engine; struct nouveau_channel *channel; + /* For PFIFO and PGRAPH. */ + spinlock_t context_switch_lock; + /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */ struct nouveau_gpuobj *ramht; uint32_t ramin_rsvd_vram; @@ -544,12 +554,6 @@ struct drm_nouveau_private { uint32_t ramro_offset; uint32_t ramro_size; - /* base physical adresses */ - uint64_t fb_phys; - uint64_t fb_available_size; - uint64_t fb_mappable_pages; - uint64_t fb_aper_free; - struct { enum { NOUVEAU_GART_NONE = 0, @@ -563,10 +567,6 @@ struct drm_nouveau_private { struct nouveau_gpuobj *sg_ctxdma; struct page *sg_dummy_page; dma_addr_t sg_dummy_bus; - - /* nottm hack */ - struct drm_ttm_backend *sg_be; - unsigned long sg_handle; } gart_info; /* nv10-nv40 tiling regions */ @@ -575,6 +575,16 @@ struct drm_nouveau_private { spinlock_t lock; } tile; + /* VRAM/fb configuration */ + uint64_t vram_size; + uint64_t vram_sys_base; + + uint64_t fb_phys; + uint64_t fb_available_size; + uint64_t fb_mappable_pages; + uint64_t fb_aper_free; + int fb_mtrr; + /* G8x/G9x virtual address space */ uint64_t vm_gart_base; uint64_t vm_gart_size; @@ -583,10 +593,6 @@ struct drm_nouveau_private { uint64_t vm_end; struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR]; int vm_vram_pt_nr; - uint64_t vram_sys_base; - - /* the mtrr covering the FB */ - int fb_mtrr; struct mem_block *ramin_heap; @@ -596,8 +602,7 @@ struct drm_nouveau_private { struct list_head gpuobj_list; - struct nvbios VBIOS; - struct nouveau_bios_info *vbios; + struct nvbios vbios; struct nv04_mode_state mode_reg; struct nv04_mode_state saved_reg; @@ -606,15 +611,10 @@ struct drm_nouveau_private { uint32_t dac_users[4]; struct nouveau_suspend_resume { - uint32_t fifo_mode; - uint32_t graph_ctx_control; - uint32_t graph_state; uint32_t *ramin_copy; - uint64_t ramin_size; } susres; struct backlight_device *backlight; - bool acpi_dsm; struct nouveau_channel *evo; @@ -674,6 +674,7 @@ extern int nouveau_uscript_tmds; extern int nouveau_vram_pushbuf; extern int nouveau_vram_notify; extern int nouveau_fbpercrtc; +extern int nouveau_tv_disable; extern char *nouveau_tv_norm; extern int nouveau_reg_debug; extern char *nouveau_vbios; @@ -681,6 +682,10 @@ extern int nouveau_ctxfw; extern int nouveau_ignorelid; extern int nouveau_nofbaccel; extern int nouveau_noaccel; +extern int nouveau_override_conntype; + +extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); +extern int nouveau_pci_resume(struct pci_dev *pdev); /* nouveau_state.c */ extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); @@ -696,12 +701,6 @@ extern bool nouveau_wait_until(struct drm_device *, uint64_t timeout, uint32_t reg, uint32_t mask, uint32_t val); extern bool nouveau_wait_for_idle(struct drm_device *); extern int nouveau_card_init(struct drm_device *); -extern int nouveau_ioctl_card_init(struct drm_device *, void *data, - struct drm_file *); -extern int nouveau_ioctl_suspend(struct drm_device *, void *data, - struct drm_file *); -extern int nouveau_ioctl_resume(struct drm_device *, void *data, - struct drm_file *); /* nouveau_mem.c */ extern int nouveau_mem_init_heap(struct mem_block **, uint64_t start, @@ -711,7 +710,7 @@ extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *, struct drm_file *, int tail); extern void nouveau_mem_takedown(struct mem_block **heap); extern void nouveau_mem_free_block(struct mem_block *); -extern uint64_t nouveau_mem_fb_amount(struct drm_device *); +extern int nouveau_mem_detect(struct drm_device *dev); extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap); extern int nouveau_mem_init(struct drm_device *); extern int nouveau_mem_init_agp(struct drm_device *); @@ -845,21 +844,15 @@ nouveau_debugfs_channel_fini(struct nouveau_channel *chan) /* nouveau_dma.c */ extern void nouveau_dma_pre_init(struct nouveau_channel *); extern int nouveau_dma_init(struct nouveau_channel *); -extern int nouveau_dma_wait(struct nouveau_channel *, int size); +extern int nouveau_dma_wait(struct nouveau_channel *, int slots, int size); /* nouveau_acpi.c */ -#ifdef CONFIG_ACPI -extern int nouveau_hybrid_setup(struct drm_device *dev); -extern bool nouveau_dsm_probe(struct drm_device *dev); +#if defined(CONFIG_ACPI) +void nouveau_register_dsm_handler(void); +void nouveau_unregister_dsm_handler(void); #else -static inline int nouveau_hybrid_setup(struct drm_device *dev) -{ - return 0; -} -static inline bool nouveau_dsm_probe(struct drm_device *dev) -{ - return false; -} +static inline void nouveau_register_dsm_handler(void) {} +static inline void nouveau_unregister_dsm_handler(void) {} #endif /* nouveau_backlight.c */ @@ -928,6 +921,10 @@ extern void nv40_fb_takedown(struct drm_device *); extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t, uint32_t, uint32_t); +/* nv50_fb.c */ +extern int nv50_fb_init(struct drm_device *); +extern void nv50_fb_takedown(struct drm_device *); + /* nv04_fifo.c */ extern int nv04_fifo_init(struct drm_device *); extern void nv04_fifo_disable(struct drm_device *); @@ -1027,6 +1024,7 @@ extern void nv50_graph_destroy_context(struct nouveau_channel *); extern int nv50_graph_load_context(struct nouveau_channel *); extern int nv50_graph_unload_context(struct drm_device *); extern void nv50_graph_context_switch(struct drm_device *); +extern int nv50_grctx_init(struct nouveau_grctx *); /* nouveau_grctx.c */ extern int nouveau_grctx_prog_load(struct drm_device *); @@ -1119,7 +1117,8 @@ extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags); extern int nouveau_bo_unpin(struct nouveau_bo *); extern int nouveau_bo_map(struct nouveau_bo *); extern void nouveau_bo_unmap(struct nouveau_bo *); -extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t memtype); +extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t type, + uint32_t busy); extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index); extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val); extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index); @@ -1152,16 +1151,6 @@ extern int nouveau_gem_ioctl_new(struct drm_device *, void *, struct drm_file *); extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *, struct drm_file *); -extern int nouveau_gem_ioctl_pushbuf_call(struct drm_device *, void *, - struct drm_file *); -extern int nouveau_gem_ioctl_pushbuf_call2(struct drm_device *, void *, - struct drm_file *); -extern int nouveau_gem_ioctl_pin(struct drm_device *, void *, - struct drm_file *); -extern int nouveau_gem_ioctl_unpin(struct drm_device *, void *, - struct drm_file *); -extern int nouveau_gem_ioctl_tile(struct drm_device *, void *, - struct drm_file *); extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *, struct drm_file *); extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, @@ -1173,6 +1162,10 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *, int nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); int nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); +/* nv50_gpio.c */ +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); + #ifndef ioread32_native #ifdef __BIG_ENDIAN #define ioread16_native ioread16be diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index bc4a24029ed1..9f28b94e479b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -47,6 +47,7 @@ struct nouveau_encoder { union { struct { + int mc_unknown; int dpcd_version; int link_nr; int link_bw; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index ea879a2efef3..8e7dc1d4912a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -30,12 +30,12 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/tty.h> -#include <linux/slab.h> #include <linux/sysrq.h> #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/screen_info.h> +#include <linux/vga_switcheroo.h> #include "drmP.h" #include "drm.h" @@ -370,6 +370,7 @@ nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width, nvbo->bo.offset, nvbo); mutex_unlock(&dev->struct_mutex); + vga_switcheroo_client_fb_set(dev->pdev, info); return 0; out_unref: @@ -401,10 +402,8 @@ nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb) unregister_framebuffer(info); nouveau_bo_unmap(nouveau_fb->nvbo); - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(nouveau_fb->nvbo->gem); + drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem); nouveau_fb->nvbo = NULL; - mutex_unlock(&dev->struct_mutex); if (par) drm_fb_helper_free(&par->helper); framebuffer_release(info); diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 70cc30803e3b..1bc0b38a5167 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -167,12 +167,10 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle); out: - mutex_lock(&dev->struct_mutex); - drm_gem_object_handle_unreference(nvbo->gem); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_handle_unreference_unlocked(nvbo->gem); if (ret) - drm_gem_object_unreference(nvbo->gem); + drm_gem_object_unreference_unlocked(nvbo->gem); return ret; } @@ -182,40 +180,35 @@ nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains, { struct nouveau_bo *nvbo = gem->driver_private; struct ttm_buffer_object *bo = &nvbo->bo; - uint64_t flags; + uint32_t domains = valid_domains & + (write_domains ? write_domains : read_domains); + uint32_t pref_flags = 0, valid_flags = 0; - if (!valid_domains || (!read_domains && !write_domains)) + if (!domains) return -EINVAL; - if (write_domains) { - if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && - (write_domains & NOUVEAU_GEM_DOMAIN_VRAM)) - flags = TTM_PL_FLAG_VRAM; - else - if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) && - (write_domains & NOUVEAU_GEM_DOMAIN_GART)) - flags = TTM_PL_FLAG_TT; - else - return -EINVAL; - } else { - if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && - (read_domains & NOUVEAU_GEM_DOMAIN_VRAM) && - bo->mem.mem_type == TTM_PL_VRAM) - flags = TTM_PL_FLAG_VRAM; - else - if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) && - (read_domains & NOUVEAU_GEM_DOMAIN_GART) && - bo->mem.mem_type == TTM_PL_TT) - flags = TTM_PL_FLAG_TT; - else - if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && - (read_domains & NOUVEAU_GEM_DOMAIN_VRAM)) - flags = TTM_PL_FLAG_VRAM; - else - flags = TTM_PL_FLAG_TT; - } + if (valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) + valid_flags |= TTM_PL_FLAG_VRAM; + + if (valid_domains & NOUVEAU_GEM_DOMAIN_GART) + valid_flags |= TTM_PL_FLAG_TT; + + if ((domains & NOUVEAU_GEM_DOMAIN_VRAM) && + bo->mem.mem_type == TTM_PL_VRAM) + pref_flags |= TTM_PL_FLAG_VRAM; + + else if ((domains & NOUVEAU_GEM_DOMAIN_GART) && + bo->mem.mem_type == TTM_PL_TT) + pref_flags |= TTM_PL_FLAG_TT; + + else if (domains & NOUVEAU_GEM_DOMAIN_VRAM) + pref_flags |= TTM_PL_FLAG_VRAM; + + else + pref_flags |= TTM_PL_FLAG_TT; + + nouveau_bo_placement_set(nvbo, pref_flags, valid_flags); - nouveau_bo_placement_set(nvbo, flags); return 0; } @@ -243,6 +236,11 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence) nouveau_fence_unref((void *)&prev_fence); } + if (unlikely(nvbo->validate_mapped)) { + ttm_bo_kunmap(&nvbo->kmap); + nvbo->validate_mapped = false; + } + list_del(&nvbo->entry); nvbo->reserved_by = NULL; ttm_bo_unreserve(&nvbo->bo); @@ -302,11 +300,14 @@ retry: if (ret == -EAGAIN) ret = ttm_bo_wait_unreserved(&nvbo->bo, false); drm_gem_object_unreference(gem); - if (ret) + if (ret) { + NV_ERROR(dev, "fail reserve\n"); return ret; + } goto retry; } + b->user_priv = (uint64_t)(unsigned long)nvbo; nvbo->reserved_by = file_priv; nvbo->pbbo_index = i; if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && @@ -336,8 +337,10 @@ retry: } ret = ttm_bo_wait_cpu(&nvbo->bo, false); - if (ret) + if (ret) { + NV_ERROR(dev, "fail wait_cpu\n"); return ret; + } goto retry; } } @@ -351,6 +354,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, { struct drm_nouveau_gem_pushbuf_bo __user *upbbo = (void __force __user *)(uintptr_t)user_pbbo_ptr; + struct drm_device *dev = chan->dev; struct nouveau_bo *nvbo; int ret, relocs = 0; @@ -362,39 +366,46 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, spin_lock(&nvbo->bo.lock); ret = ttm_bo_wait(&nvbo->bo, false, false, false); spin_unlock(&nvbo->bo.lock); - if (unlikely(ret)) + if (unlikely(ret)) { + NV_ERROR(dev, "fail wait other chan\n"); return ret; + } } ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains, b->write_domains, b->valid_domains); - if (unlikely(ret)) + if (unlikely(ret)) { + NV_ERROR(dev, "fail set_domain\n"); return ret; + } nvbo->channel = chan; ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement, false, false); nvbo->channel = NULL; - if (unlikely(ret)) + if (unlikely(ret)) { + NV_ERROR(dev, "fail ttm_validate\n"); return ret; + } - if (nvbo->bo.offset == b->presumed_offset && + if (nvbo->bo.offset == b->presumed.offset && ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && - b->presumed_domain & NOUVEAU_GEM_DOMAIN_VRAM) || + b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || (nvbo->bo.mem.mem_type == TTM_PL_TT && - b->presumed_domain & NOUVEAU_GEM_DOMAIN_GART))) + b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART))) continue; if (nvbo->bo.mem.mem_type == TTM_PL_TT) - b->presumed_domain = NOUVEAU_GEM_DOMAIN_GART; + b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; else - b->presumed_domain = NOUVEAU_GEM_DOMAIN_VRAM; - b->presumed_offset = nvbo->bo.offset; - b->presumed_ok = 0; + b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; + b->presumed.offset = nvbo->bo.offset; + b->presumed.valid = 0; relocs++; - if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index], b, sizeof(*b))) + if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed, + &b->presumed, sizeof(b->presumed))) return -EFAULT; } @@ -408,6 +419,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, uint64_t user_buffers, int nr_buffers, struct validate_op *op, int *apply_relocs) { + struct drm_device *dev = chan->dev; int ret, relocs = 0; INIT_LIST_HEAD(&op->vram_list); @@ -418,11 +430,14 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, return 0; ret = validate_init(chan, file_priv, pbbo, nr_buffers, op); - if (unlikely(ret)) + if (unlikely(ret)) { + NV_ERROR(dev, "validate_init\n"); return ret; + } ret = validate_list(chan, &op->vram_list, pbbo, user_buffers); if (unlikely(ret < 0)) { + NV_ERROR(dev, "validate vram_list\n"); validate_fini(op, NULL); return ret; } @@ -430,6 +445,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, ret = validate_list(chan, &op->gart_list, pbbo, user_buffers); if (unlikely(ret < 0)) { + NV_ERROR(dev, "validate gart_list\n"); validate_fini(op, NULL); return ret; } @@ -437,6 +453,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, ret = validate_list(chan, &op->both_list, pbbo, user_buffers); if (unlikely(ret < 0)) { + NV_ERROR(dev, "validate both_list\n"); validate_fini(op, NULL); return ret; } @@ -465,59 +482,82 @@ u_memcpya(uint64_t user, unsigned nmemb, unsigned size) } static int -nouveau_gem_pushbuf_reloc_apply(struct nouveau_channel *chan, int nr_bo, - struct drm_nouveau_gem_pushbuf_bo *bo, - unsigned nr_relocs, uint64_t ptr_relocs, - unsigned nr_dwords, unsigned first_dword, - uint32_t *pushbuf, bool is_iomem) +nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev, + struct drm_nouveau_gem_pushbuf *req, + struct drm_nouveau_gem_pushbuf_bo *bo) { struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL; - struct drm_device *dev = chan->dev; int ret = 0; unsigned i; - reloc = u_memcpya(ptr_relocs, nr_relocs, sizeof(*reloc)); + reloc = u_memcpya(req->relocs, req->nr_relocs, sizeof(*reloc)); if (IS_ERR(reloc)) return PTR_ERR(reloc); - for (i = 0; i < nr_relocs; i++) { + for (i = 0; i < req->nr_relocs; i++) { struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i]; struct drm_nouveau_gem_pushbuf_bo *b; + struct nouveau_bo *nvbo; uint32_t data; - if (r->bo_index >= nr_bo || r->reloc_index < first_dword || - r->reloc_index >= first_dword + nr_dwords) { - NV_ERROR(dev, "Bad relocation %d\n", i); - NV_ERROR(dev, " bo: %d max %d\n", r->bo_index, nr_bo); - NV_ERROR(dev, " id: %d max %d\n", r->reloc_index, nr_dwords); + if (unlikely(r->bo_index > req->nr_buffers)) { + NV_ERROR(dev, "reloc bo index invalid\n"); ret = -EINVAL; break; } b = &bo[r->bo_index]; - if (b->presumed_ok) + if (b->presumed.valid) continue; + if (unlikely(r->reloc_bo_index > req->nr_buffers)) { + NV_ERROR(dev, "reloc container bo index invalid\n"); + ret = -EINVAL; + break; + } + nvbo = (void *)(unsigned long)bo[r->reloc_bo_index].user_priv; + + if (unlikely(r->reloc_bo_offset + 4 > + nvbo->bo.mem.num_pages << PAGE_SHIFT)) { + NV_ERROR(dev, "reloc outside of bo\n"); + ret = -EINVAL; + break; + } + + if (!nvbo->kmap.virtual) { + ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, + &nvbo->kmap); + if (ret) { + NV_ERROR(dev, "failed kmap for reloc\n"); + break; + } + nvbo->validate_mapped = true; + } + if (r->flags & NOUVEAU_GEM_RELOC_LOW) - data = b->presumed_offset + r->data; + data = b->presumed.offset + r->data; else if (r->flags & NOUVEAU_GEM_RELOC_HIGH) - data = (b->presumed_offset + r->data) >> 32; + data = (b->presumed.offset + r->data) >> 32; else data = r->data; if (r->flags & NOUVEAU_GEM_RELOC_OR) { - if (b->presumed_domain == NOUVEAU_GEM_DOMAIN_GART) + if (b->presumed.domain == NOUVEAU_GEM_DOMAIN_GART) data |= r->tor; else data |= r->vor; } - if (is_iomem) - iowrite32_native(data, (void __force __iomem *) - &pushbuf[r->reloc_index]); - else - pushbuf[r->reloc_index] = data; + spin_lock(&nvbo->bo.lock); + ret = ttm_bo_wait(&nvbo->bo, false, false, false); + spin_unlock(&nvbo->bo.lock); + if (ret) { + NV_ERROR(dev, "reloc wait_idle failed: %d\n", ret); + break; + } + + nouveau_bo_wr32(nvbo, r->reloc_bo_offset >> 2, data); } kfree(reloc); @@ -528,127 +568,50 @@ int nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_gem_pushbuf *req = data; - struct drm_nouveau_gem_pushbuf_bo *bo = NULL; + struct drm_nouveau_gem_pushbuf_push *push; + struct drm_nouveau_gem_pushbuf_bo *bo; struct nouveau_channel *chan; struct validate_op op; - struct nouveau_fence* fence = 0; - uint32_t *pushbuf = NULL; - int ret = 0, do_reloc = 0, i; + struct nouveau_fence *fence = 0; + int i, j, ret = 0, do_reloc = 0; NOUVEAU_CHECK_INITIALISED_WITH_RETURN; NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan); - if (req->nr_dwords >= chan->dma.max || - req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS || - req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) { - NV_ERROR(dev, "Pushbuf config exceeds limits:\n"); - NV_ERROR(dev, " dwords : %d max %d\n", req->nr_dwords, - chan->dma.max - 1); - NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers, - NOUVEAU_GEM_MAX_BUFFERS); - NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs, - NOUVEAU_GEM_MAX_RELOCS); - return -EINVAL; - } - - pushbuf = u_memcpya(req->dwords, req->nr_dwords, sizeof(uint32_t)); - if (IS_ERR(pushbuf)) - return PTR_ERR(pushbuf); - - bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo)); - if (IS_ERR(bo)) { - kfree(pushbuf); - return PTR_ERR(bo); - } - - mutex_lock(&dev->struct_mutex); - - /* Validate buffer list */ - ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers, - req->nr_buffers, &op, &do_reloc); - if (ret) - goto out; - - /* Apply any relocations that are required */ - if (do_reloc) { - ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, - bo, req->nr_relocs, - req->relocs, - req->nr_dwords, 0, - pushbuf, false); - if (ret) - goto out; - } - - /* Emit push buffer to the hw - */ - ret = RING_SPACE(chan, req->nr_dwords); - if (ret) - goto out; - - OUT_RINGp(chan, pushbuf, req->nr_dwords); + req->vram_available = dev_priv->fb_aper_free; + req->gart_available = dev_priv->gart_info.aper_free; + if (unlikely(req->nr_push == 0)) + goto out_next; - ret = nouveau_fence_new(chan, &fence, true); - if (ret) { - NV_ERROR(dev, "error fencing pushbuf: %d\n", ret); - WIND_RING(chan); - goto out; + if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) { + NV_ERROR(dev, "pushbuf push count exceeds limit: %d max %d\n", + req->nr_push, NOUVEAU_GEM_MAX_PUSH); + return -EINVAL; } - if (nouveau_gem_pushbuf_sync(chan)) { - ret = nouveau_fence_wait(fence, NULL, false, false); - if (ret) { - for (i = 0; i < req->nr_dwords; i++) - NV_ERROR(dev, "0x%08x\n", pushbuf[i]); - NV_ERROR(dev, "^^ above push buffer is fail :(\n"); - } + if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) { + NV_ERROR(dev, "pushbuf bo count exceeds limit: %d max %d\n", + req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS); + return -EINVAL; } -out: - validate_fini(&op, fence); - nouveau_fence_unref((void**)&fence); - mutex_unlock(&dev->struct_mutex); - kfree(pushbuf); - kfree(bo); - return ret; -} - -#define PUSHBUF_CAL (dev_priv->card_type >= NV_20) - -int -nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct drm_nouveau_gem_pushbuf_call *req = data; - struct drm_nouveau_gem_pushbuf_bo *bo = NULL; - struct nouveau_channel *chan; - struct drm_gem_object *gem; - struct nouveau_bo *pbbo; - struct validate_op op; - struct nouveau_fence* fence = 0; - int i, ret = 0, do_reloc = 0; - - NOUVEAU_CHECK_INITIALISED_WITH_RETURN; - NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan); - - if (unlikely(req->handle == 0)) - goto out_next; - - if (req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS || - req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) { - NV_ERROR(dev, "Pushbuf config exceeds limits:\n"); - NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers, - NOUVEAU_GEM_MAX_BUFFERS); - NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs, - NOUVEAU_GEM_MAX_RELOCS); + if (unlikely(req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS)) { + NV_ERROR(dev, "pushbuf reloc count exceeds limit: %d max %d\n", + req->nr_relocs, NOUVEAU_GEM_MAX_RELOCS); return -EINVAL; } + push = u_memcpya(req->push, req->nr_push, sizeof(*push)); + if (IS_ERR(push)) + return PTR_ERR(push); + bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo)); - if (IS_ERR(bo)) + if (IS_ERR(bo)) { + kfree(push); return PTR_ERR(bo); + } mutex_lock(&dev->struct_mutex); @@ -660,122 +623,84 @@ nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data, goto out; } - /* Validate DMA push buffer */ - gem = drm_gem_object_lookup(dev, file_priv, req->handle); - if (!gem) { - NV_ERROR(dev, "Unknown pb handle 0x%08x\n", req->handle); - ret = -EINVAL; - goto out; - } - pbbo = nouveau_gem_object(gem); - - if ((req->offset & 3) || req->nr_dwords < 2 || - (unsigned long)req->offset > (unsigned long)pbbo->bo.mem.size || - (unsigned long)req->nr_dwords > - ((unsigned long)(pbbo->bo.mem.size - req->offset ) >> 2)) { - NV_ERROR(dev, "pb call misaligned or out of bounds: " - "%d + %d * 4 > %ld\n", - req->offset, req->nr_dwords, pbbo->bo.mem.size); - ret = -EINVAL; - drm_gem_object_unreference(gem); - goto out; - } - - ret = ttm_bo_reserve(&pbbo->bo, false, false, true, - chan->fence.sequence); - if (ret) { - NV_ERROR(dev, "resv pb: %d\n", ret); - drm_gem_object_unreference(gem); - goto out; - } - - nouveau_bo_placement_set(pbbo, 1 << chan->pushbuf_bo->bo.mem.mem_type); - ret = ttm_bo_validate(&pbbo->bo, &pbbo->placement, false, false); - if (ret) { - NV_ERROR(dev, "validate pb: %d\n", ret); - ttm_bo_unreserve(&pbbo->bo); - drm_gem_object_unreference(gem); - goto out; - } - - list_add_tail(&pbbo->entry, &op.both_list); - - /* If presumed return address doesn't match, we need to map the - * push buffer and fix it.. - */ - if (!PUSHBUF_CAL) { - uint32_t retaddy; - - if (chan->dma.free < 4 + NOUVEAU_DMA_SKIPS) { - ret = nouveau_dma_wait(chan, 4 + NOUVEAU_DMA_SKIPS); - if (ret) { - NV_ERROR(dev, "jmp_space: %d\n", ret); - goto out; - } - } - - retaddy = chan->pushbuf_base + ((chan->dma.cur + 2) << 2); - retaddy |= 0x20000000; - if (retaddy != req->suffix0) { - req->suffix0 = retaddy; - do_reloc = 1; - } - } - /* Apply any relocations that are required */ if (do_reloc) { - void *pbvirt; - bool is_iomem; - ret = ttm_bo_kmap(&pbbo->bo, 0, pbbo->bo.mem.num_pages, - &pbbo->kmap); + ret = nouveau_gem_pushbuf_reloc_apply(dev, req, bo); if (ret) { - NV_ERROR(dev, "kmap pb: %d\n", ret); + NV_ERROR(dev, "reloc apply: %d\n", ret); goto out; } + } - pbvirt = ttm_kmap_obj_virtual(&pbbo->kmap, &is_iomem); - ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, bo, - req->nr_relocs, - req->relocs, - req->nr_dwords, - req->offset / 4, - pbvirt, is_iomem); - - if (!PUSHBUF_CAL) { - nouveau_bo_wr32(pbbo, - req->offset / 4 + req->nr_dwords - 2, - req->suffix0); - } - - ttm_bo_kunmap(&pbbo->kmap); + if (chan->dma.ib_max) { + ret = nouveau_dma_wait(chan, req->nr_push + 1, 6); if (ret) { - NV_ERROR(dev, "reloc apply: %d\n", ret); + NV_INFO(dev, "nv50cal_space: %d\n", ret); goto out; } - } - if (PUSHBUF_CAL) { - ret = RING_SPACE(chan, 2); + for (i = 0; i < req->nr_push; i++) { + struct nouveau_bo *nvbo = (void *)(unsigned long) + bo[push[i].bo_index].user_priv; + + nv50_dma_push(chan, nvbo, push[i].offset, + push[i].length); + } + } else + if (dev_priv->card_type >= NV_20) { + ret = RING_SPACE(chan, req->nr_push * 2); if (ret) { NV_ERROR(dev, "cal_space: %d\n", ret); goto out; } - OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) + - req->offset) | 2); - OUT_RING(chan, 0); + + for (i = 0; i < req->nr_push; i++) { + struct nouveau_bo *nvbo = (void *)(unsigned long) + bo[push[i].bo_index].user_priv; + struct drm_mm_node *mem = nvbo->bo.mem.mm_node; + + OUT_RING(chan, ((mem->start << PAGE_SHIFT) + + push[i].offset) | 2); + OUT_RING(chan, 0); + } } else { - ret = RING_SPACE(chan, 2 + NOUVEAU_DMA_SKIPS); + ret = RING_SPACE(chan, req->nr_push * (2 + NOUVEAU_DMA_SKIPS)); if (ret) { NV_ERROR(dev, "jmp_space: %d\n", ret); goto out; } - OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) + - req->offset) | 0x20000000); - OUT_RING(chan, 0); - /* Space the jumps apart with NOPs. */ - for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) + for (i = 0; i < req->nr_push; i++) { + struct nouveau_bo *nvbo = (void *)(unsigned long) + bo[push[i].bo_index].user_priv; + struct drm_mm_node *mem = nvbo->bo.mem.mm_node; + uint32_t cmd; + + cmd = chan->pushbuf_base + ((chan->dma.cur + 2) << 2); + cmd |= 0x20000000; + if (unlikely(cmd != req->suffix0)) { + if (!nvbo->kmap.virtual) { + ret = ttm_bo_kmap(&nvbo->bo, 0, + nvbo->bo.mem. + num_pages, + &nvbo->kmap); + if (ret) { + WIND_RING(chan); + goto out; + } + nvbo->validate_mapped = true; + } + + nouveau_bo_wr32(nvbo, (push[i].offset + + push[i].length - 8) / 4, cmd); + } + + OUT_RING(chan, ((mem->start << PAGE_SHIFT) + + push[i].offset) | 0x20000000); OUT_RING(chan, 0); + for (j = 0; j < NOUVEAU_DMA_SKIPS; j++) + OUT_RING(chan, 0); + } } ret = nouveau_fence_new(chan, &fence, true); @@ -790,9 +715,14 @@ out: nouveau_fence_unref((void**)&fence); mutex_unlock(&dev->struct_mutex); kfree(bo); + kfree(push); out_next: - if (PUSHBUF_CAL) { + if (chan->dma.ib_max) { + req->suffix0 = 0x00000000; + req->suffix1 = 0x00000000; + } else + if (dev_priv->card_type >= NV_20) { req->suffix0 = 0x00020000; req->suffix1 = 0x00000000; } else { @@ -804,19 +734,6 @@ out_next: return ret; } -int -nouveau_gem_ioctl_pushbuf_call2(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct drm_nouveau_gem_pushbuf_call *req = data; - - req->vram_available = dev_priv->fb_aper_free; - req->gart_available = dev_priv->gart_info.aper_free; - - return nouveau_gem_ioctl_pushbuf_call(dev, data, file_priv); -} - static inline uint32_t domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain) { @@ -831,74 +748,6 @@ domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain) } int -nouveau_gem_ioctl_pin(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_nouveau_gem_pin *req = data; - struct drm_gem_object *gem; - struct nouveau_bo *nvbo; - int ret = 0; - - NOUVEAU_CHECK_INITIALISED_WITH_RETURN; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - NV_ERROR(dev, "pin only allowed without kernel modesetting\n"); - return -EINVAL; - } - - if (!DRM_SUSER(DRM_CURPROC)) - return -EPERM; - - gem = drm_gem_object_lookup(dev, file_priv, req->handle); - if (!gem) - return -EINVAL; - nvbo = nouveau_gem_object(gem); - - ret = nouveau_bo_pin(nvbo, domain_to_ttm(nvbo, req->domain)); - if (ret) - goto out; - - req->offset = nvbo->bo.offset; - if (nvbo->bo.mem.mem_type == TTM_PL_TT) - req->domain = NOUVEAU_GEM_DOMAIN_GART; - else - req->domain = NOUVEAU_GEM_DOMAIN_VRAM; - -out: - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&dev->struct_mutex); - - return ret; -} - -int -nouveau_gem_ioctl_unpin(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_nouveau_gem_pin *req = data; - struct drm_gem_object *gem; - int ret; - - NOUVEAU_CHECK_INITIALISED_WITH_RETURN; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - gem = drm_gem_object_lookup(dev, file_priv, req->handle); - if (!gem) - return -EINVAL; - - ret = nouveau_bo_unpin(nouveau_gem_object(gem)); - - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&dev->struct_mutex); - - return ret; -} - -int nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -935,9 +784,7 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, } out: - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(gem); return ret; } @@ -965,9 +812,7 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data, ret = 0; out: - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(gem); return ret; } @@ -986,9 +831,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data, return -EINVAL; ret = nouveau_gem_info(gem, req); - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(gem); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.c b/drivers/gpu/drm/nouveau/nouveau_grctx.c index c7ebec696747..32f0e495464c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_grctx.c +++ b/drivers/gpu/drm/nouveau/nouveau_grctx.c @@ -23,6 +23,7 @@ */ #include <linux/firmware.h> +#include <linux/slab.h> #include "drmP.h" #include "nouveau_drv.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c index dc46792a5c96..7855b35effc3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hw.c +++ b/drivers/gpu/drm/nouveau/nouveau_hw.c @@ -160,7 +160,7 @@ static void setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv) { struct drm_nouveau_private *dev_priv = dev->dev_private; - int chip_version = dev_priv->vbios->chip_version; + int chip_version = dev_priv->vbios.chip_version; uint32_t oldpll = NVReadRAMDAC(dev, 0, reg); int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff; uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1; @@ -216,7 +216,7 @@ setPLL_double_highregs(struct drm_device *dev, uint32_t reg1, struct nouveau_pll_vals *pv) { struct drm_nouveau_private *dev_priv = dev->dev_private; - int chip_version = dev_priv->vbios->chip_version; + int chip_version = dev_priv->vbios.chip_version; bool nv3035 = chip_version == 0x30 || chip_version == 0x35; uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70); uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1); @@ -374,7 +374,7 @@ nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1, struct nouveau_pll_vals *pv) { struct drm_nouveau_private *dev_priv = dev->dev_private; - int cv = dev_priv->vbios->chip_version; + int cv = dev_priv->vbios.chip_version; if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || cv >= 0x40) { diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index 70e994d28122..88583e7bf651 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -254,16 +254,16 @@ struct nouveau_i2c_chan * nouveau_i2c_find(struct drm_device *dev, int index) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; - if (index > DCB_MAX_NUM_I2C_ENTRIES) + if (index >= DCB_MAX_NUM_I2C_ENTRIES) return NULL; - if (!bios->bdcb.dcb.i2c[index].chan) { - if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index)) + if (!bios->dcb.i2c[index].chan) { + if (nouveau_i2c_init(dev, &bios->dcb.i2c[index], index)) return NULL; } - return bios->bdcb.dcb.i2c[index].chan; + return bios->dcb.i2c[index].chan; } diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c index 447f9f69d6b1..13e73cee4c44 100644 --- a/drivers/gpu/drm/nouveau/nouveau_irq.c +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c @@ -51,6 +51,7 @@ nouveau_irq_preinstall(struct drm_device *dev) if (dev_priv->card_type == NV_50) { INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh); + INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh); INIT_LIST_HEAD(&dev_priv->vbl_waiting); } } @@ -311,6 +312,31 @@ nouveau_print_bitfield_names_(uint32_t value, #define nouveau_print_bitfield_names(val, namelist) \ nouveau_print_bitfield_names_((val), (namelist), ARRAY_SIZE(namelist)) +struct nouveau_enum_names { + uint32_t value; + const char *name; +}; + +static void +nouveau_print_enum_names_(uint32_t value, + const struct nouveau_enum_names *namelist, + const int namelist_len) +{ + /* + * Caller must have already printed the KERN_* log level for us. + * Also the caller is responsible for adding the newline. + */ + int i; + for (i = 0; i < namelist_len; ++i) { + if (value == namelist[i].value) { + printk("%s", namelist[i].name); + return; + } + } + printk("unknown value 0x%08x", value); +} +#define nouveau_print_enum_names(val, namelist) \ + nouveau_print_enum_names_((val), (namelist), ARRAY_SIZE(namelist)) static int nouveau_graph_chid_from_grctx(struct drm_device *dev) @@ -427,14 +453,16 @@ nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id, struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t nsource = trap->nsource, nstatus = trap->nstatus; - NV_INFO(dev, "%s - nSource:", id); - nouveau_print_bitfield_names(nsource, nsource_names); - printk(", nStatus:"); - if (dev_priv->card_type < NV_10) - nouveau_print_bitfield_names(nstatus, nstatus_names); - else - nouveau_print_bitfield_names(nstatus, nstatus_names_nv10); - printk("\n"); + if (dev_priv->card_type < NV_50) { + NV_INFO(dev, "%s - nSource:", id); + nouveau_print_bitfield_names(nsource, nsource_names); + printk(", nStatus:"); + if (dev_priv->card_type < NV_10) + nouveau_print_bitfield_names(nstatus, nstatus_names); + else + nouveau_print_bitfield_names(nstatus, nstatus_names_nv10); + printk("\n"); + } NV_INFO(dev, "%s - Ch %d/%d Class 0x%04x Mthd 0x%04x " "Data 0x%08x:0x%08x\n", @@ -578,27 +606,502 @@ nouveau_pgraph_irq_handler(struct drm_device *dev) } static void +nv50_pfb_vm_trap(struct drm_device *dev, int display, const char *name) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t trap[6]; + int i, ch; + uint32_t idx = nv_rd32(dev, 0x100c90); + if (idx & 0x80000000) { + idx &= 0xffffff; + if (display) { + for (i = 0; i < 6; i++) { + nv_wr32(dev, 0x100c90, idx | i << 24); + trap[i] = nv_rd32(dev, 0x100c94); + } + for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { + struct nouveau_channel *chan = dev_priv->fifos[ch]; + + if (!chan || !chan->ramin) + continue; + + if (trap[1] == chan->ramin->instance >> 12) + break; + } + NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x %08x channel %d\n", + name, (trap[5]&0x100?"read":"write"), + trap[5]&0xff, trap[4]&0xffff, + trap[3]&0xffff, trap[0], trap[2], ch); + } + nv_wr32(dev, 0x100c90, idx | 0x80000000); + } else if (display) { + NV_INFO(dev, "%s - no VM fault?\n", name); + } +} + +static struct nouveau_enum_names nv50_mp_exec_error_names[] = +{ + { 3, "STACK_UNDERFLOW" }, + { 4, "QUADON_ACTIVE" }, + { 8, "TIMEOUT" }, + { 0x10, "INVALID_OPCODE" }, + { 0x40, "BREAKPOINT" }, +}; + +static void +nv50_pgraph_mp_trap(struct drm_device *dev, int tpid, int display) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t units = nv_rd32(dev, 0x1540); + uint32_t addr, mp10, status, pc, oplow, ophigh; + int i; + int mps = 0; + for (i = 0; i < 4; i++) { + if (!(units & 1 << (i+24))) + continue; + if (dev_priv->chipset < 0xa0) + addr = 0x408200 + (tpid << 12) + (i << 7); + else + addr = 0x408100 + (tpid << 11) + (i << 7); + mp10 = nv_rd32(dev, addr + 0x10); + status = nv_rd32(dev, addr + 0x14); + if (!status) + continue; + if (display) { + nv_rd32(dev, addr + 0x20); + pc = nv_rd32(dev, addr + 0x24); + oplow = nv_rd32(dev, addr + 0x70); + ophigh= nv_rd32(dev, addr + 0x74); + NV_INFO(dev, "PGRAPH_TRAP_MP_EXEC - " + "TP %d MP %d: ", tpid, i); + nouveau_print_enum_names(status, + nv50_mp_exec_error_names); + printk(" at %06x warp %d, opcode %08x %08x\n", + pc&0xffffff, pc >> 24, + oplow, ophigh); + } + nv_wr32(dev, addr + 0x10, mp10); + nv_wr32(dev, addr + 0x14, 0); + mps++; + } + if (!mps && display) + NV_INFO(dev, "PGRAPH_TRAP_MP_EXEC - TP %d: " + "No MPs claiming errors?\n", tpid); +} + +static void +nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old, + uint32_t ustatus_new, int display, const char *name) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + int tps = 0; + uint32_t units = nv_rd32(dev, 0x1540); + int i, r; + uint32_t ustatus_addr, ustatus; + for (i = 0; i < 16; i++) { + if (!(units & (1 << i))) + continue; + if (dev_priv->chipset < 0xa0) + ustatus_addr = ustatus_old + (i << 12); + else + ustatus_addr = ustatus_new + (i << 11); + ustatus = nv_rd32(dev, ustatus_addr) & 0x7fffffff; + if (!ustatus) + continue; + tps++; + switch (type) { + case 6: /* texture error... unknown for now */ + nv50_pfb_vm_trap(dev, display, name); + if (display) { + NV_ERROR(dev, "magic set %d:\n", i); + for (r = ustatus_addr + 4; r <= ustatus_addr + 0x10; r += 4) + NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, + nv_rd32(dev, r)); + } + break; + case 7: /* MP error */ + if (ustatus & 0x00010000) { + nv50_pgraph_mp_trap(dev, i, display); + ustatus &= ~0x00010000; + } + break; + case 8: /* TPDMA error */ + { + uint32_t e0c = nv_rd32(dev, ustatus_addr + 4); + uint32_t e10 = nv_rd32(dev, ustatus_addr + 8); + uint32_t e14 = nv_rd32(dev, ustatus_addr + 0xc); + uint32_t e18 = nv_rd32(dev, ustatus_addr + 0x10); + uint32_t e1c = nv_rd32(dev, ustatus_addr + 0x14); + uint32_t e20 = nv_rd32(dev, ustatus_addr + 0x18); + uint32_t e24 = nv_rd32(dev, ustatus_addr + 0x1c); + nv50_pfb_vm_trap(dev, display, name); + /* 2d engine destination */ + if (ustatus & 0x00000010) { + if (display) { + NV_INFO(dev, "PGRAPH_TRAP_TPDMA_2D - TP %d - Unknown fault at address %02x%08x\n", + i, e14, e10); + NV_INFO(dev, "PGRAPH_TRAP_TPDMA_2D - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n", + i, e0c, e18, e1c, e20, e24); + } + ustatus &= ~0x00000010; + } + /* Render target */ + if (ustatus & 0x00000040) { + if (display) { + NV_INFO(dev, "PGRAPH_TRAP_TPDMA_RT - TP %d - Unknown fault at address %02x%08x\n", + i, e14, e10); + NV_INFO(dev, "PGRAPH_TRAP_TPDMA_RT - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n", + i, e0c, e18, e1c, e20, e24); + } + ustatus &= ~0x00000040; + } + /* CUDA memory: l[], g[] or stack. */ + if (ustatus & 0x00000080) { + if (display) { + if (e18 & 0x80000000) { + /* g[] read fault? */ + NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - Global read fault at address %02x%08x\n", + i, e14, e10 | ((e18 >> 24) & 0x1f)); + e18 &= ~0x1f000000; + } else if (e18 & 0xc) { + /* g[] write fault? */ + NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - Global write fault at address %02x%08x\n", + i, e14, e10 | ((e18 >> 7) & 0x1f)); + e18 &= ~0x00000f80; + } else { + NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - Unknown CUDA fault at address %02x%08x\n", + i, e14, e10); + } + NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n", + i, e0c, e18, e1c, e20, e24); + } + ustatus &= ~0x00000080; + } + } + break; + } + if (ustatus) { + if (display) + NV_INFO(dev, "%s - TP%d: Unhandled ustatus 0x%08x\n", name, i, ustatus); + } + nv_wr32(dev, ustatus_addr, 0xc0000000); + } + + if (!tps && display) + NV_INFO(dev, "%s - No TPs claiming errors?\n", name); +} + +static void +nv50_pgraph_trap_handler(struct drm_device *dev) +{ + struct nouveau_pgraph_trap trap; + uint32_t status = nv_rd32(dev, 0x400108); + uint32_t ustatus; + int display = nouveau_ratelimit(); + + + if (!status && display) { + nouveau_graph_trap_info(dev, &trap); + nouveau_graph_dump_trap_info(dev, "PGRAPH_TRAP", &trap); + NV_INFO(dev, "PGRAPH_TRAP - no units reporting traps?\n"); + } + + /* DISPATCH: Relays commands to other units and handles NOTIFY, + * COND, QUERY. If you get a trap from it, the command is still stuck + * in DISPATCH and you need to do something about it. */ + if (status & 0x001) { + ustatus = nv_rd32(dev, 0x400804) & 0x7fffffff; + if (!ustatus && display) { + NV_INFO(dev, "PGRAPH_TRAP_DISPATCH - no ustatus?\n"); + } + + /* Known to be triggered by screwed up NOTIFY and COND... */ + if (ustatus & 0x00000001) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_FAULT"); + nv_wr32(dev, 0x400500, 0); + if (nv_rd32(dev, 0x400808) & 0x80000000) { + if (display) { + if (nouveau_graph_trapped_channel(dev, &trap.channel)) + trap.channel = -1; + trap.class = nv_rd32(dev, 0x400814); + trap.mthd = nv_rd32(dev, 0x400808) & 0x1ffc; + trap.subc = (nv_rd32(dev, 0x400808) >> 16) & 0x7; + trap.data = nv_rd32(dev, 0x40080c); + trap.data2 = nv_rd32(dev, 0x400810); + nouveau_graph_dump_trap_info(dev, + "PGRAPH_TRAP_DISPATCH_FAULT", &trap); + NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_FAULT - 400808: %08x\n", nv_rd32(dev, 0x400808)); + NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_FAULT - 400848: %08x\n", nv_rd32(dev, 0x400848)); + } + nv_wr32(dev, 0x400808, 0); + } else if (display) { + NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_FAULT - No stuck command?\n"); + } + nv_wr32(dev, 0x4008e8, nv_rd32(dev, 0x4008e8) & 3); + nv_wr32(dev, 0x400848, 0); + ustatus &= ~0x00000001; + } + if (ustatus & 0x00000002) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_QUERY"); + nv_wr32(dev, 0x400500, 0); + if (nv_rd32(dev, 0x40084c) & 0x80000000) { + if (display) { + if (nouveau_graph_trapped_channel(dev, &trap.channel)) + trap.channel = -1; + trap.class = nv_rd32(dev, 0x400814); + trap.mthd = nv_rd32(dev, 0x40084c) & 0x1ffc; + trap.subc = (nv_rd32(dev, 0x40084c) >> 16) & 0x7; + trap.data = nv_rd32(dev, 0x40085c); + trap.data2 = 0; + nouveau_graph_dump_trap_info(dev, + "PGRAPH_TRAP_DISPATCH_QUERY", &trap); + NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_QUERY - 40084c: %08x\n", nv_rd32(dev, 0x40084c)); + } + nv_wr32(dev, 0x40084c, 0); + } else if (display) { + NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_QUERY - No stuck command?\n"); + } + ustatus &= ~0x00000002; + } + if (ustatus && display) + NV_INFO(dev, "PGRAPH_TRAP_DISPATCH - Unhandled ustatus 0x%08x\n", ustatus); + nv_wr32(dev, 0x400804, 0xc0000000); + nv_wr32(dev, 0x400108, 0x001); + status &= ~0x001; + } + + /* TRAPs other than dispatch use the "normal" trap regs. */ + if (status && display) { + nouveau_graph_trap_info(dev, &trap); + nouveau_graph_dump_trap_info(dev, + "PGRAPH_TRAP", &trap); + } + + /* M2MF: Memory to memory copy engine. */ + if (status & 0x002) { + ustatus = nv_rd32(dev, 0x406800) & 0x7fffffff; + if (!ustatus && display) { + NV_INFO(dev, "PGRAPH_TRAP_M2MF - no ustatus?\n"); + } + if (ustatus & 0x00000001) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_NOTIFY"); + ustatus &= ~0x00000001; + } + if (ustatus & 0x00000002) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_IN"); + ustatus &= ~0x00000002; + } + if (ustatus & 0x00000004) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_OUT"); + ustatus &= ~0x00000004; + } + NV_INFO (dev, "PGRAPH_TRAP_M2MF - %08x %08x %08x %08x\n", + nv_rd32(dev, 0x406804), + nv_rd32(dev, 0x406808), + nv_rd32(dev, 0x40680c), + nv_rd32(dev, 0x406810)); + if (ustatus && display) + NV_INFO(dev, "PGRAPH_TRAP_M2MF - Unhandled ustatus 0x%08x\n", ustatus); + /* No sane way found yet -- just reset the bugger. */ + nv_wr32(dev, 0x400040, 2); + nv_wr32(dev, 0x400040, 0); + nv_wr32(dev, 0x406800, 0xc0000000); + nv_wr32(dev, 0x400108, 0x002); + status &= ~0x002; + } + + /* VFETCH: Fetches data from vertex buffers. */ + if (status & 0x004) { + ustatus = nv_rd32(dev, 0x400c04) & 0x7fffffff; + if (!ustatus && display) { + NV_INFO(dev, "PGRAPH_TRAP_VFETCH - no ustatus?\n"); + } + if (ustatus & 0x00000001) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_VFETCH_FAULT"); + NV_INFO (dev, "PGRAPH_TRAP_VFETCH_FAULT - %08x %08x %08x %08x\n", + nv_rd32(dev, 0x400c00), + nv_rd32(dev, 0x400c08), + nv_rd32(dev, 0x400c0c), + nv_rd32(dev, 0x400c10)); + ustatus &= ~0x00000001; + } + if (ustatus && display) + NV_INFO(dev, "PGRAPH_TRAP_VFETCH - Unhandled ustatus 0x%08x\n", ustatus); + nv_wr32(dev, 0x400c04, 0xc0000000); + nv_wr32(dev, 0x400108, 0x004); + status &= ~0x004; + } + + /* STRMOUT: DirectX streamout / OpenGL transform feedback. */ + if (status & 0x008) { + ustatus = nv_rd32(dev, 0x401800) & 0x7fffffff; + if (!ustatus && display) { + NV_INFO(dev, "PGRAPH_TRAP_STRMOUT - no ustatus?\n"); + } + if (ustatus & 0x00000001) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_STRMOUT_FAULT"); + NV_INFO (dev, "PGRAPH_TRAP_STRMOUT_FAULT - %08x %08x %08x %08x\n", + nv_rd32(dev, 0x401804), + nv_rd32(dev, 0x401808), + nv_rd32(dev, 0x40180c), + nv_rd32(dev, 0x401810)); + ustatus &= ~0x00000001; + } + if (ustatus && display) + NV_INFO(dev, "PGRAPH_TRAP_STRMOUT - Unhandled ustatus 0x%08x\n", ustatus); + /* No sane way found yet -- just reset the bugger. */ + nv_wr32(dev, 0x400040, 0x80); + nv_wr32(dev, 0x400040, 0); + nv_wr32(dev, 0x401800, 0xc0000000); + nv_wr32(dev, 0x400108, 0x008); + status &= ~0x008; + } + + /* CCACHE: Handles code and c[] caches and fills them. */ + if (status & 0x010) { + ustatus = nv_rd32(dev, 0x405018) & 0x7fffffff; + if (!ustatus && display) { + NV_INFO(dev, "PGRAPH_TRAP_CCACHE - no ustatus?\n"); + } + if (ustatus & 0x00000001) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_CCACHE_FAULT"); + NV_INFO (dev, "PGRAPH_TRAP_CCACHE_FAULT - %08x %08x %08x %08x %08x %08x %08x\n", + nv_rd32(dev, 0x405800), + nv_rd32(dev, 0x405804), + nv_rd32(dev, 0x405808), + nv_rd32(dev, 0x40580c), + nv_rd32(dev, 0x405810), + nv_rd32(dev, 0x405814), + nv_rd32(dev, 0x40581c)); + ustatus &= ~0x00000001; + } + if (ustatus && display) + NV_INFO(dev, "PGRAPH_TRAP_CCACHE - Unhandled ustatus 0x%08x\n", ustatus); + nv_wr32(dev, 0x405018, 0xc0000000); + nv_wr32(dev, 0x400108, 0x010); + status &= ~0x010; + } + + /* Unknown, not seen yet... 0x402000 is the only trap status reg + * remaining, so try to handle it anyway. Perhaps related to that + * unknown DMA slot on tesla? */ + if (status & 0x20) { + nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_UNKC04"); + ustatus = nv_rd32(dev, 0x402000) & 0x7fffffff; + if (display) + NV_INFO(dev, "PGRAPH_TRAP_UNKC04 - Unhandled ustatus 0x%08x\n", ustatus); + nv_wr32(dev, 0x402000, 0xc0000000); + /* no status modifiction on purpose */ + } + + /* TEXTURE: CUDA texturing units */ + if (status & 0x040) { + nv50_pgraph_tp_trap (dev, 6, 0x408900, 0x408600, display, + "PGRAPH_TRAP_TEXTURE"); + nv_wr32(dev, 0x400108, 0x040); + status &= ~0x040; + } + + /* MP: CUDA execution engines. */ + if (status & 0x080) { + nv50_pgraph_tp_trap (dev, 7, 0x408314, 0x40831c, display, + "PGRAPH_TRAP_MP"); + nv_wr32(dev, 0x400108, 0x080); + status &= ~0x080; + } + + /* TPDMA: Handles TP-initiated uncached memory accesses: + * l[], g[], stack, 2d surfaces, render targets. */ + if (status & 0x100) { + nv50_pgraph_tp_trap (dev, 8, 0x408e08, 0x408708, display, + "PGRAPH_TRAP_TPDMA"); + nv_wr32(dev, 0x400108, 0x100); + status &= ~0x100; + } + + if (status) { + if (display) + NV_INFO(dev, "PGRAPH_TRAP - Unknown trap 0x%08x\n", + status); + nv_wr32(dev, 0x400108, status); + } +} + +/* There must be a *lot* of these. Will take some time to gather them up. */ +static struct nouveau_enum_names nv50_data_error_names[] = +{ + { 4, "INVALID_VALUE" }, + { 5, "INVALID_ENUM" }, + { 8, "INVALID_OBJECT" }, + { 0xc, "INVALID_BITFIELD" }, + { 0x28, "MP_NO_REG_SPACE" }, + { 0x2b, "MP_BLOCK_SIZE_MISMATCH" }, +}; + +static void nv50_pgraph_irq_handler(struct drm_device *dev) { + struct nouveau_pgraph_trap trap; + int unhandled = 0; uint32_t status; while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) { - uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE); - + /* NOTIFY: You've set a NOTIFY an a command and it's done. */ if (status & 0x00000001) { - nouveau_pgraph_intr_notify(dev, nsource); + nouveau_graph_trap_info(dev, &trap); + if (nouveau_ratelimit()) + nouveau_graph_dump_trap_info(dev, + "PGRAPH_NOTIFY", &trap); status &= ~0x00000001; nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000001); } - if (status & 0x00000010) { - nouveau_pgraph_intr_error(dev, nsource | - NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD); + /* COMPUTE_QUERY: Purpose and exact cause unknown, happens + * when you write 0x200 to 0x50c0 method 0x31c. */ + if (status & 0x00000002) { + nouveau_graph_trap_info(dev, &trap); + if (nouveau_ratelimit()) + nouveau_graph_dump_trap_info(dev, + "PGRAPH_COMPUTE_QUERY", &trap); + status &= ~0x00000002; + nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000002); + } + + /* Unknown, never seen: 0x4 */ + /* ILLEGAL_MTHD: You used a wrong method for this class. */ + if (status & 0x00000010) { + nouveau_graph_trap_info(dev, &trap); + if (nouveau_pgraph_intr_swmthd(dev, &trap)) + unhandled = 1; + if (unhandled && nouveau_ratelimit()) + nouveau_graph_dump_trap_info(dev, + "PGRAPH_ILLEGAL_MTHD", &trap); status &= ~0x00000010; nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000010); } + /* ILLEGAL_CLASS: You used a wrong class. */ + if (status & 0x00000020) { + nouveau_graph_trap_info(dev, &trap); + if (nouveau_ratelimit()) + nouveau_graph_dump_trap_info(dev, + "PGRAPH_ILLEGAL_CLASS", &trap); + status &= ~0x00000020; + nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000020); + } + + /* DOUBLE_NOTIFY: You tried to set a NOTIFY on another NOTIFY. */ + if (status & 0x00000040) { + nouveau_graph_trap_info(dev, &trap); + if (nouveau_ratelimit()) + nouveau_graph_dump_trap_info(dev, + "PGRAPH_DOUBLE_NOTIFY", &trap); + status &= ~0x00000040; + nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000040); + } + + /* CONTEXT_SWITCH: PGRAPH needs us to load a new context */ if (status & 0x00001000) { nv_wr32(dev, 0x400500, 0x00000000); nv_wr32(dev, NV03_PGRAPH_INTR, @@ -613,49 +1116,59 @@ nv50_pgraph_irq_handler(struct drm_device *dev) status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; } - if (status & 0x00100000) { - nouveau_pgraph_intr_error(dev, nsource | - NV03_PGRAPH_NSOURCE_DATA_ERROR); + /* BUFFER_NOTIFY: Your m2mf transfer finished */ + if (status & 0x00010000) { + nouveau_graph_trap_info(dev, &trap); + if (nouveau_ratelimit()) + nouveau_graph_dump_trap_info(dev, + "PGRAPH_BUFFER_NOTIFY", &trap); + status &= ~0x00010000; + nv_wr32(dev, NV03_PGRAPH_INTR, 0x00010000); + } + /* DATA_ERROR: Invalid value for this method, or invalid + * state in current PGRAPH context for this operation */ + if (status & 0x00100000) { + nouveau_graph_trap_info(dev, &trap); + if (nouveau_ratelimit()) { + nouveau_graph_dump_trap_info(dev, + "PGRAPH_DATA_ERROR", &trap); + NV_INFO (dev, "PGRAPH_DATA_ERROR - "); + nouveau_print_enum_names(nv_rd32(dev, 0x400110), + nv50_data_error_names); + printk("\n"); + } status &= ~0x00100000; nv_wr32(dev, NV03_PGRAPH_INTR, 0x00100000); } + /* TRAP: Something bad happened in the middle of command + * execution. Has a billion types, subtypes, and even + * subsubtypes. */ if (status & 0x00200000) { - int r; - - nouveau_pgraph_intr_error(dev, nsource | - NV03_PGRAPH_NSOURCE_PROTECTION_ERROR); - - NV_ERROR(dev, "magic set 1:\n"); - for (r = 0x408900; r <= 0x408910; r += 4) - NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, - nv_rd32(dev, r)); - nv_wr32(dev, 0x408900, - nv_rd32(dev, 0x408904) | 0xc0000000); - for (r = 0x408e08; r <= 0x408e24; r += 4) - NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, - nv_rd32(dev, r)); - nv_wr32(dev, 0x408e08, - nv_rd32(dev, 0x408e08) | 0xc0000000); - - NV_ERROR(dev, "magic set 2:\n"); - for (r = 0x409900; r <= 0x409910; r += 4) - NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, - nv_rd32(dev, r)); - nv_wr32(dev, 0x409900, - nv_rd32(dev, 0x409904) | 0xc0000000); - for (r = 0x409e08; r <= 0x409e24; r += 4) - NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, - nv_rd32(dev, r)); - nv_wr32(dev, 0x409e08, - nv_rd32(dev, 0x409e08) | 0xc0000000); - + nv50_pgraph_trap_handler(dev); status &= ~0x00200000; - nv_wr32(dev, NV03_PGRAPH_NSOURCE, nsource); nv_wr32(dev, NV03_PGRAPH_INTR, 0x00200000); } + /* Unknown, never seen: 0x00400000 */ + + /* SINGLE_STEP: Happens on every method if you turned on + * single stepping in 40008c */ + if (status & 0x01000000) { + nouveau_graph_trap_info(dev, &trap); + if (nouveau_ratelimit()) + nouveau_graph_dump_trap_info(dev, + "PGRAPH_SINGLE_STEP", &trap); + status &= ~0x01000000; + nv_wr32(dev, NV03_PGRAPH_INTR, 0x01000000); + } + + /* 0x02000000 happens when you pause a ctxprog... + * but the only way this can happen that I know is by + * poking the relevant MMIO register, and we don't + * do that. */ + if (status) { NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status); @@ -672,7 +1185,8 @@ nv50_pgraph_irq_handler(struct drm_device *dev) } nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING); - nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31)); + if (nv_rd32(dev, 0x400824) & (1 << 31)) + nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31)); } static void @@ -691,11 +1205,14 @@ nouveau_irq_handler(DRM_IRQ_ARGS) struct drm_device *dev = (struct drm_device *)arg; struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t status, fbdev_flags = 0; + unsigned long flags; status = nv_rd32(dev, NV03_PMC_INTR_0); if (!status) return IRQ_NONE; + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + if (dev_priv->fbdev_info) { fbdev_flags = dev_priv->fbdev_info->flags; dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED; @@ -733,5 +1250,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS) if (dev_priv->fbdev_info) dev_priv->fbdev_info->flags = fbdev_flags; + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + return IRQ_HANDLED; } diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 2dc09dbd817d..775a7017af64 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -347,6 +347,20 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, return -EBUSY; } + nv_wr32(dev, 0x100c80, 0x00040001); + if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); + NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); + return -EBUSY; + } + + nv_wr32(dev, 0x100c80, 0x00060001); + if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); + NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); + return -EBUSY; + } + return 0; } @@ -387,6 +401,20 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); + return; + } + + nv_wr32(dev, 0x100c80, 0x00040001); + if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); + NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); + return; + } + + nv_wr32(dev, 0x100c80, 0x00060001); + if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); + NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); } } @@ -449,9 +477,30 @@ void nouveau_mem_close(struct drm_device *dev) } } -/*XXX won't work on BSD because of pci_read_config_dword */ static uint32_t -nouveau_mem_fb_amount_igp(struct drm_device *dev) +nouveau_mem_detect_nv04(struct drm_device *dev) +{ + uint32_t boot0 = nv_rd32(dev, NV03_BOOT_0); + + if (boot0 & 0x00000100) + return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024; + + switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) { + case NV04_BOOT_0_RAM_AMOUNT_32MB: + return 32 * 1024 * 1024; + case NV04_BOOT_0_RAM_AMOUNT_16MB: + return 16 * 1024 * 1024; + case NV04_BOOT_0_RAM_AMOUNT_8MB: + return 8 * 1024 * 1024; + case NV04_BOOT_0_RAM_AMOUNT_4MB: + return 4 * 1024 * 1024; + } + + return 0; +} + +static uint32_t +nouveau_mem_detect_nforce(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct pci_dev *bridge; @@ -463,11 +512,11 @@ nouveau_mem_fb_amount_igp(struct drm_device *dev) return 0; } - if (dev_priv->flags&NV_NFORCE) { + if (dev_priv->flags & NV_NFORCE) { pci_read_config_dword(bridge, 0x7C, &mem); return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024; } else - if (dev_priv->flags&NV_NFORCE2) { + if (dev_priv->flags & NV_NFORCE2) { pci_read_config_dword(bridge, 0x84, &mem); return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024; } @@ -477,50 +526,32 @@ nouveau_mem_fb_amount_igp(struct drm_device *dev) } /* returns the amount of FB ram in bytes */ -uint64_t nouveau_mem_fb_amount(struct drm_device *dev) +int +nouveau_mem_detect(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t boot0; - - switch (dev_priv->card_type) { - case NV_04: - boot0 = nv_rd32(dev, NV03_BOOT_0); - if (boot0 & 0x00000100) - return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024; - - switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) { - case NV04_BOOT_0_RAM_AMOUNT_32MB: - return 32 * 1024 * 1024; - case NV04_BOOT_0_RAM_AMOUNT_16MB: - return 16 * 1024 * 1024; - case NV04_BOOT_0_RAM_AMOUNT_8MB: - return 8 * 1024 * 1024; - case NV04_BOOT_0_RAM_AMOUNT_4MB: - return 4 * 1024 * 1024; - } - break; - case NV_10: - case NV_20: - case NV_30: - case NV_40: - case NV_50: - default: - if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) { - return nouveau_mem_fb_amount_igp(dev); - } else { - uint64_t mem; - mem = (nv_rd32(dev, NV04_FIFO_DATA) & - NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK) >> - NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT; - return mem * 1024 * 1024; - } - break; + + if (dev_priv->card_type == NV_04) { + dev_priv->vram_size = nouveau_mem_detect_nv04(dev); + } else + if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) { + dev_priv->vram_size = nouveau_mem_detect_nforce(dev); + } else { + dev_priv->vram_size = nv_rd32(dev, NV04_FIFO_DATA); + dev_priv->vram_size &= NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK; + if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) + dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10) << 12; } - NV_ERROR(dev, - "Unable to detect video ram size. Please report your setup to " - DRIVER_EMAIL "\n"); - return 0; + NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20)); + if (dev_priv->vram_sys_base) { + NV_INFO(dev, "Stolen system memory at: 0x%010llx\n", + dev_priv->vram_sys_base); + } + + if (dev_priv->vram_size) + return 0; + return -ENOMEM; } #if __OS_HAS_AGP @@ -631,15 +662,12 @@ nouveau_mem_init(struct drm_device *dev) spin_lock_init(&dev_priv->ttm.bo_list_lock); spin_lock_init(&dev_priv->tile.lock); - dev_priv->fb_available_size = nouveau_mem_fb_amount(dev); - + dev_priv->fb_available_size = dev_priv->vram_size; dev_priv->fb_mappable_pages = dev_priv->fb_available_size; if (dev_priv->fb_mappable_pages > drm_get_resource_len(dev, 1)) dev_priv->fb_mappable_pages = drm_get_resource_len(dev, 1); dev_priv->fb_mappable_pages >>= PAGE_SHIFT; - NV_INFO(dev, "%d MiB VRAM\n", (int)(dev_priv->fb_available_size >> 20)); - /* remove reserved space at end of vram from available amount */ dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram; dev_priv->fb_aper_free = dev_priv->fb_available_size; diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c index d99dc087f9b1..9537f3e30115 100644 --- a/drivers/gpu/drm/nouveau/nouveau_notifier.c +++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c @@ -61,11 +61,8 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan) chan->notifier_bo = ntfy; out_err: - if (ret) { - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(ntfy->gem); - mutex_unlock(&dev->struct_mutex); - } + if (ret) + drm_gem_object_unreference_unlocked(ntfy->gem); return ret; } @@ -81,8 +78,8 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan) nouveau_bo_unmap(chan->notifier_bo); mutex_lock(&dev->struct_mutex); nouveau_bo_unpin(chan->notifier_bo); - drm_gem_object_unreference(chan->notifier_bo->gem); mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(chan->notifier_bo->gem); nouveau_mem_takedown(&chan->notifier_heap); } diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index ed1590577b6c..1d6ee8b55154 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -1,6 +1,7 @@ #include "drmP.h" #include "nouveau_drv.h" #include <linux/pagemap.h> +#include <linux/slab.h> #define NV_CTXDMA_PAGE_SHIFT 12 #define NV_CTXDMA_PAGE_SIZE (1 << NV_CTXDMA_PAGE_SHIFT) @@ -171,6 +172,24 @@ nouveau_sgdma_unbind(struct ttm_backend *be) } dev_priv->engine.instmem.finish_access(nvbe->dev); + if (dev_priv->card_type == NV_50) { + nv_wr32(dev, 0x100c80, 0x00050001); + if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); + NV_ERROR(dev, "0x100c80 = 0x%08x\n", + nv_rd32(dev, 0x100c80)); + return -EBUSY; + } + + nv_wr32(dev, 0x100c80, 0x00000001); + if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); + NV_ERROR(dev, "0x100c80 = 0x%08x\n", + nv_rd32(dev, 0x100c80)); + return -EBUSY; + } + } + nvbe->bound = false; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index a4851af5b05e..e1710640a278 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -24,17 +24,18 @@ */ #include <linux/swab.h> +#include <linux/slab.h> #include "drmP.h" #include "drm.h" #include "drm_sarea.h" #include "drm_crtc_helper.h" #include <linux/vgaarb.h> +#include <linux/vga_switcheroo.h> #include "nouveau_drv.h" #include "nouveau_drm.h" #include "nv50_display.h" -static int nouveau_stub_init(struct drm_device *dev) { return 0; } static void nouveau_stub_takedown(struct drm_device *dev) {} static int nouveau_init_engine_ptrs(struct drm_device *dev) @@ -276,8 +277,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.init = nv04_timer_init; engine->timer.read = nv04_timer_read; engine->timer.takedown = nv04_timer_takedown; - engine->fb.init = nouveau_stub_init; - engine->fb.takedown = nouveau_stub_takedown; + engine->fb.init = nv50_fb_init; + engine->fb.takedown = nv50_fb_takedown; engine->graph.grclass = nv50_graph_grclass; engine->graph.init = nv50_graph_init; engine->graph.takedown = nv50_graph_takedown; @@ -340,7 +341,7 @@ nouveau_card_init_channel(struct drm_device *dev) gpuobj = NULL; ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY, - 0, nouveau_mem_fb_amount(dev), + 0, dev_priv->vram_size, NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM, &gpuobj); if (ret) @@ -371,6 +372,30 @@ out_err: return ret; } +static void nouveau_switcheroo_set_state(struct pci_dev *pdev, + enum vga_switcheroo_state state) +{ + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; + if (state == VGA_SWITCHEROO_ON) { + printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); + nouveau_pci_resume(pdev); + } else { + printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); + nouveau_pci_suspend(pdev, pmm); + } +} + +static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + bool can_switch; + + spin_lock(&dev->count_lock); + can_switch = (dev->open_count == 0); + spin_unlock(&dev->count_lock); + return can_switch; +} + int nouveau_card_init(struct drm_device *dev) { @@ -384,6 +409,8 @@ nouveau_card_init(struct drm_device *dev) return 0; vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); + vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state, + nouveau_switcheroo_can_switch); /* Initialise internal driver API hooks */ ret = nouveau_init_engine_ptrs(dev); @@ -391,6 +418,7 @@ nouveau_card_init(struct drm_device *dev) goto out; engine = &dev_priv->engine; dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED; + spin_lock_init(&dev_priv->context_switch_lock); /* Parse BIOS tables / Run init tables if card not POSTed */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { @@ -399,6 +427,10 @@ nouveau_card_init(struct drm_device *dev) goto out; } + ret = nouveau_mem_detect(dev); + if (ret) + goto out_bios; + ret = nouveau_gpuobj_early_init(dev); if (ret) goto out_bios; @@ -474,7 +506,7 @@ nouveau_card_init(struct drm_device *dev) else ret = nv04_display_create(dev); if (ret) - goto out_irq; + goto out_channel; } ret = nouveau_backlight_init(dev); @@ -488,6 +520,11 @@ nouveau_card_init(struct drm_device *dev) return 0; +out_channel: + if (dev_priv->channel) { + nouveau_channel_free(dev_priv->channel); + dev_priv->channel = NULL; + } out_irq: drm_irq_uninstall(dev); out_fifo: @@ -505,6 +542,7 @@ out_mc: out_gpuobj: nouveau_gpuobj_takedown(dev); out_mem: + nouveau_sgdma_takedown(dev); nouveau_mem_close(dev); out_instmem: engine->instmem.takedown(dev); @@ -617,11 +655,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", dev->pci_vendor, dev->pci_device, dev->pdev->class); - dev_priv->acpi_dsm = nouveau_dsm_probe(dev); - - if (dev_priv->acpi_dsm) - nouveau_hybrid_setup(dev); - dev_priv->wq = create_workqueue("nouveau"); if (!dev_priv->wq) return -EINVAL; @@ -776,13 +809,6 @@ int nouveau_unload(struct drm_device *dev) return 0; } -int -nouveau_ioctl_card_init(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - return nouveau_card_init(dev); -} - int nouveau_ioctl_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index d2f143ed97c1..eba687f1099e 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -230,9 +230,9 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode) struct drm_framebuffer *fb = crtc->fb; /* Calculate our timings */ - int horizDisplay = (mode->crtc_hdisplay >> 3) - 1; - int horizStart = (mode->crtc_hsync_start >> 3) - 1; - int horizEnd = (mode->crtc_hsync_end >> 3) - 1; + int horizDisplay = (mode->crtc_hdisplay >> 3) - 1; + int horizStart = (mode->crtc_hsync_start >> 3) + 1; + int horizEnd = (mode->crtc_hsync_end >> 3) + 1; int horizTotal = (mode->crtc_htotal >> 3) - 5; int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1; int horizBlankEnd = (mode->crtc_htotal >> 3) - 1; @@ -926,9 +926,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); nv_crtc->cursor.show(nv_crtc, true); out: - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(gem); return ret; } diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c index 1d73b15d70da..1cb19e3acb55 100644 --- a/drivers/gpu/drm/nouveau/nv04_dac.c +++ b/drivers/gpu/drm/nouveau/nv04_dac.c @@ -230,13 +230,13 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) if (dcb->type == OUTPUT_TV) { testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); - if (dev_priv->vbios->tvdactestval) - testval = dev_priv->vbios->tvdactestval; + if (dev_priv->vbios.tvdactestval) + testval = dev_priv->vbios.tvdactestval; } else { testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ - if (dev_priv->vbios->dactestval) - testval = dev_priv->vbios->dactestval; + if (dev_priv->vbios.dactestval) + testval = dev_priv->vbios.dactestval; } saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index 483f875bdb6a..41634d4752fe 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -269,10 +269,10 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; if (!nv_gf4_disp_arch(dev) || (output_mode->hsync_start - output_mode->hdisplay) >= - dev_priv->vbios->digital_min_front_porch) + dev_priv->vbios.digital_min_front_porch) regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay; else - regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1; + regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios.digital_min_front_porch - 1; regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1; regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew; diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index ef77215fa5b9..c7898b4f6dfb 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -93,10 +93,9 @@ int nv04_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct parsed_dcb *dcb = dev_priv->vbios->dcb; + struct dcb_table *dcb = &dev_priv->vbios.dcb; struct drm_encoder *encoder; struct drm_crtc *crtc; - uint16_t connector[16] = { 0 }; int i, ret; NV_DEBUG_KMS(dev, "\n"); @@ -154,52 +153,10 @@ nv04_display_create(struct drm_device *dev) if (ret) continue; - - connector[dcbent->connector] |= (1 << dcbent->type); } - for (i = 0; i < dcb->entries; i++) { - struct dcb_entry *dcbent = &dcb->entry[i]; - uint16_t encoders; - int type; - - encoders = connector[dcbent->connector]; - if (!(encoders & (1 << dcbent->type))) - continue; - connector[dcbent->connector] = 0; - - switch (dcbent->type) { - case OUTPUT_ANALOG: - if (!MULTIPLE_ENCODERS(encoders)) - type = DRM_MODE_CONNECTOR_VGA; - else - type = DRM_MODE_CONNECTOR_DVII; - break; - case OUTPUT_TMDS: - if (!MULTIPLE_ENCODERS(encoders)) - type = DRM_MODE_CONNECTOR_DVID; - else - type = DRM_MODE_CONNECTOR_DVII; - break; - case OUTPUT_LVDS: - type = DRM_MODE_CONNECTOR_LVDS; -#if 0 - /* don't create i2c adapter when lvds ddc not allowed */ - if (dcbent->lvdsconf.use_straps_for_mode || - dev_priv->vbios->fp_no_ddc) - i2c_index = 0xf; -#endif - break; - case OUTPUT_TV: - type = DRM_MODE_CONNECTOR_TV; - break; - default: - type = DRM_MODE_CONNECTOR_Unknown; - continue; - } - - nouveau_connector_create(dev, dcbent->connector, type); - } + for (i = 0; i < dcb->connector.entries; i++) + nouveau_connector_create(dev, &dcb->connector.entry[i]); /* Save previous state */ NVLockVgaCrtcs(dev, false); diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c index fd01caabd5c3..813b25cec726 100644 --- a/drivers/gpu/drm/nouveau/nv04_fbcon.c +++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c @@ -118,8 +118,8 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) return; } - width = (image->width + 31) & ~31; - dsize = (width * image->height) >> 5; + width = ALIGN(image->width, 8); + dsize = ALIGN(width * image->height, 32) >> 5; if (info->fix.visual == FB_VISUAL_TRUECOLOR || info->fix.visual == FB_VISUAL_DIRECTCOLOR) { @@ -136,8 +136,8 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) ((image->dx + image->width) & 0xffff)); OUT_RING(chan, bg); OUT_RING(chan, fg); - OUT_RING(chan, (image->height << 16) | image->width); OUT_RING(chan, (image->height << 16) | width); + OUT_RING(chan, (image->height << 16) | image->width); OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff)); while (dsize) { diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c index f31347b8c9b0..66fe55983b6e 100644 --- a/drivers/gpu/drm/nouveau/nv04_fifo.c +++ b/drivers/gpu/drm/nouveau/nv04_fifo.c @@ -117,6 +117,7 @@ nv04_fifo_create_context(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; + unsigned long flags; int ret; ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0, @@ -127,6 +128,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan) if (ret) return ret; + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + /* Setup initial state */ dev_priv->engine.instmem.prepare_access(dev, true); RAMFC_WR(DMA_PUT, chan->pushbuf_base); @@ -144,6 +147,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan) /* enable the fifo dma operation */ nv_wr32(dev, NV04_PFIFO_MODE, nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); + + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c index 9c63099e9c42..c4e3404337d4 100644 --- a/drivers/gpu/drm/nouveau/nv04_tv.c +++ b/drivers/gpu/drm/nouveau/nv04_tv.c @@ -262,7 +262,7 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry) nv_encoder->or = ffs(entry->or) - 1; /* Run the slave-specific initialization */ - adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter; + adap = &dev_priv->vbios.dcb.i2c[i2c_index].chan->adapter; was_locked = NVLockVgaCrtcs(dev, false); diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 21ac6e49b6ee..74c880374fb9 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -45,8 +45,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) testval = RGB_TEST_DATA(0x82, 0xeb, 0x82); - if (dev_priv->vbios->tvdactestval) - testval = dev_priv->vbios->tvdactestval; + if (dev_priv->vbios.tvdactestval) + testval = dev_priv->vbios.tvdactestval; dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); head = (dacclk & 0x100) >> 8; @@ -367,7 +367,7 @@ static void nv17_tv_prepare(struct drm_encoder *encoder) !enc->crtc && nv04_dfp_get_bound_head(dev, dcb) == head) { nv04_dfp_bind_head(dev, dcb, head ^ 1, - dev_priv->VBIOS.fp.dual_link); + dev_priv->vbios.fp.dual_link); } } diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c index b4f19ccb8b41..500ccfd3a0b8 100644 --- a/drivers/gpu/drm/nouveau/nv40_fifo.c +++ b/drivers/gpu/drm/nouveau/nv40_fifo.c @@ -37,6 +37,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan) struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t fc = NV40_RAMFC(chan->id); + unsigned long flags; int ret; ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0, @@ -45,6 +46,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan) if (ret) return ret; + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + dev_priv->engine.instmem.prepare_access(dev, true); nv_wi32(dev, fc + 0, chan->pushbuf_base); nv_wi32(dev, fc + 4, chan->pushbuf_base); @@ -63,6 +66,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan) /* enable the fifo dma operation */ nv_wr32(dev, NV04_PFIFO_MODE, nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); + + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); return 0; } @@ -273,7 +278,7 @@ nv40_fifo_init_ramxx(struct drm_device *dev) default: nv_wr32(dev, 0x2230, 0); nv_wr32(dev, NV40_PFIFO_RAMFC, - ((nouveau_mem_fb_amount(dev) - 512 * 1024 + + ((dev_priv->vram_size - 512 * 1024 + dev_priv->ramfc_offset) >> 16) | (3 << 16)); break; } diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c index 53e8afe1dcd1..0616c96e4b67 100644 --- a/drivers/gpu/drm/nouveau/nv40_graph.c +++ b/drivers/gpu/drm/nouveau/nv40_graph.c @@ -335,6 +335,27 @@ nv40_graph_init(struct drm_device *dev) nv_wr32(dev, 0x400b38, 0x2ffff800); nv_wr32(dev, 0x400b3c, 0x00006000); + /* Tiling related stuff. */ + switch (dev_priv->chipset) { + case 0x44: + case 0x4a: + nv_wr32(dev, 0x400bc4, 0x1003d888); + nv_wr32(dev, 0x400bbc, 0xb7a7b500); + break; + case 0x46: + nv_wr32(dev, 0x400bc4, 0x0000e024); + nv_wr32(dev, 0x400bbc, 0xb7a7b520); + break; + case 0x4c: + case 0x4e: + case 0x67: + nv_wr32(dev, 0x400bc4, 0x1003d888); + nv_wr32(dev, 0x400bbc, 0xb7a7b540); + break; + default: + break; + } + /* Turn all the tiling regions off. */ for (i = 0; i < pfb->num_tiles; i++) nv40_graph_set_region_tiling(dev, i, 0, 0, 0); diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index d1a651e3400c..cfabeb974a56 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -358,9 +358,7 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, nv_crtc->cursor.show(nv_crtc, true); out: - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(gem); return ret; } diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c index f08f042a8e10..1fd9537beff6 100644 --- a/drivers/gpu/drm/nouveau/nv50_dac.c +++ b/drivers/gpu/drm/nouveau/nv50_dac.c @@ -79,8 +79,8 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) } /* Use bios provided value if possible. */ - if (dev_priv->vbios->dactestval) { - load_pattern = dev_priv->vbios->dactestval; + if (dev_priv->vbios.dactestval) { + load_pattern = dev_priv->vbios.dactestval; NV_DEBUG_KMS(dev, "Using bios provided load_pattern of %d\n", load_pattern); } else { diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 90f0bf59fbcd..649db4c1b690 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -143,7 +143,7 @@ nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan) } ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoVRAM, 0, 0x19, - 0, nouveau_mem_fb_amount(dev)); + 0, dev_priv->vram_size); if (ret) { nv50_evo_channel_del(pchan); return ret; @@ -231,7 +231,7 @@ nv50_display_init(struct drm_device *dev) /* This used to be in crtc unblank, but seems out of place there. */ nv_wr32(dev, NV50_PDISPLAY_UNK_380, 0); /* RAM is clamped to 256 MiB. */ - ram_amount = nouveau_mem_fb_amount(dev); + ram_amount = dev_priv->vram_size; NV_DEBUG_KMS(dev, "ram_amount %d\n", ram_amount); if (ram_amount > 256*1024*1024) ram_amount = 256*1024*1024; @@ -370,9 +370,7 @@ nv50_display_init(struct drm_device *dev) struct nouveau_connector *conn = nouveau_connector(connector); struct dcb_gpio_entry *gpio; - if (connector->connector_type != DRM_MODE_CONNECTOR_DVII && - connector->connector_type != DRM_MODE_CONNECTOR_DVID && - connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) + if (conn->dcb->gpio_tag == 0xff) continue; gpio = nouveau_bios_gpio_entry(dev, conn->dcb->gpio_tag); @@ -465,8 +463,7 @@ static int nv50_display_disable(struct drm_device *dev) int nv50_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct parsed_dcb *dcb = dev_priv->vbios->dcb; - uint32_t connector[16] = {}; + struct dcb_table *dcb = &dev_priv->vbios.dcb; int ret, i; NV_DEBUG_KMS(dev, "\n"); @@ -522,49 +519,20 @@ int nv50_display_create(struct drm_device *dev) NV_WARN(dev, "DCB encoder %d unknown\n", entry->type); continue; } - - connector[entry->connector] |= (1 << entry->type); } - /* It appears that DCB 3.0+ VBIOS has a connector table, however, - * I'm not 100% certain how to decode it correctly yet so just - * look at what encoders are present on each connector index and - * attempt to derive the connector type from that. - */ - for (i = 0 ; i < dcb->entries; i++) { - struct dcb_entry *entry = &dcb->entry[i]; - uint16_t encoders; - int type; - - encoders = connector[entry->connector]; - if (!(encoders & (1 << entry->type))) + for (i = 0 ; i < dcb->connector.entries; i++) { + if (i != 0 && dcb->connector.entry[i].index2 == + dcb->connector.entry[i - 1].index2) continue; - connector[entry->connector] = 0; - - if (encoders & (1 << OUTPUT_DP)) { - type = DRM_MODE_CONNECTOR_DisplayPort; - } else if (encoders & (1 << OUTPUT_TMDS)) { - if (encoders & (1 << OUTPUT_ANALOG)) - type = DRM_MODE_CONNECTOR_DVII; - else - type = DRM_MODE_CONNECTOR_DVID; - } else if (encoders & (1 << OUTPUT_ANALOG)) { - type = DRM_MODE_CONNECTOR_VGA; - } else if (encoders & (1 << OUTPUT_LVDS)) { - type = DRM_MODE_CONNECTOR_LVDS; - } else { - type = DRM_MODE_CONNECTOR_Unknown; - } - - if (type == DRM_MODE_CONNECTOR_Unknown) - continue; - - nouveau_connector_create(dev, entry->connector, type); + nouveau_connector_create(dev, &dcb->connector.entry[i]); } ret = nv50_display_init(dev); - if (ret) + if (ret) { + nv50_display_destroy(dev); return ret; + } return 0; } @@ -667,8 +635,8 @@ nv50_display_irq_head(struct drm_device *dev, int *phead, return -1; } - for (i = 0; i < dev_priv->vbios->dcb->entries; i++) { - struct dcb_entry *dcbent = &dev_priv->vbios->dcb->entry[i]; + for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { + struct dcb_entry *dcbent = &dev_priv->vbios.dcb.entry[i]; if (dcbent->type != type) continue; @@ -692,7 +660,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent, struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_connector *nv_connector = NULL; struct drm_encoder *encoder; - struct nvbios *bios = &dev_priv->VBIOS; + struct nvbios *bios = &dev_priv->vbios; uint32_t mc, script = 0, or; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { @@ -710,7 +678,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent, switch (dcbent->type) { case OUTPUT_LVDS: script = (mc >> 8) & 0xf; - if (bios->pub.fp_no_ddc) { + if (bios->fp_no_ddc) { if (bios->fp.dual_link) script |= 0x0100; if (bios->fp.if_is_24bit) @@ -919,10 +887,12 @@ nv50_display_error_handler(struct drm_device *dev) nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000); } -static void -nv50_display_irq_hotplug(struct drm_device *dev) +void +nv50_display_irq_hotplug_bh(struct work_struct *work) { - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct drm_nouveau_private *dev_priv = + container_of(work, struct drm_nouveau_private, hpd_work); + struct drm_device *dev = dev_priv->dev; struct drm_connector *connector; const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; uint32_t unplug_mask, plug_mask, change_mask; @@ -983,8 +953,10 @@ nv50_display_irq_handler(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t delayed = 0; - while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) - nv50_display_irq_hotplug(dev); + if (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) { + if (!work_pending(&dev_priv->hpd_work)) + queue_work(dev_priv->wq, &dev_priv->hpd_work); + } while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index 3ae8d0725f63..581d405ac014 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -37,6 +37,7 @@ void nv50_display_irq_handler(struct drm_device *dev); void nv50_display_irq_handler_bh(struct work_struct *work); +void nv50_display_irq_hotplug_bh(struct work_struct *work); int nv50_display_init(struct drm_device *dev); int nv50_display_create(struct drm_device *dev); int nv50_display_destroy(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c new file mode 100644 index 000000000000..a95e6941ba88 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_fb.c @@ -0,0 +1,32 @@ +#include "drmP.h" +#include "drm.h" +#include "nouveau_drv.h" +#include "nouveau_drm.h" + +int +nv50_fb_init(struct drm_device *dev) +{ + /* This is needed to get meaningful information from 100c90 + * on traps. No idea what these values mean exactly. */ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + switch (dev_priv->chipset) { + case 0x50: + nv_wr32(dev, 0x100c90, 0x0707ff); + break; + case 0xa5: + case 0xa8: + nv_wr32(dev, 0x100c90, 0x0d0fff); + break; + default: + nv_wr32(dev, 0x100c90, 0x1d07ff); + break; + } + + return 0; +} + +void +nv50_fb_takedown(struct drm_device *dev) +{ +} diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c index 0f57cdf7ccb2..a8c70e7e9184 100644 --- a/drivers/gpu/drm/nouveau/nv50_fbcon.c +++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c @@ -109,7 +109,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) return; } - width = (image->width + 31) & ~31; + width = ALIGN(image->width, 32); dwords = (width * image->height) >> 5; BEGIN_RING(chan, NvSub2D, 0x0814, 2); @@ -157,8 +157,11 @@ nv50_fbcon_accel_init(struct fb_info *info) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = dev_priv->channel; struct nouveau_gpuobj *eng2d = NULL; + uint64_t fb; int ret, format; + fb = info->fix.smem_start - dev_priv->fb_phys + dev_priv->vm_vram_base; + switch (info->var.bits_per_pixel) { case 8: format = 0xf3; @@ -233,7 +236,7 @@ nv50_fbcon_accel_init(struct fb_info *info) BEGIN_RING(chan, NvSub2D, 0x0808, 3); OUT_RING(chan, 0); OUT_RING(chan, 0); - OUT_RING(chan, 0); + OUT_RING(chan, 1); BEGIN_RING(chan, NvSub2D, 0x081c, 1); OUT_RING(chan, 1); BEGIN_RING(chan, NvSub2D, 0x0840, 4); @@ -248,9 +251,8 @@ nv50_fbcon_accel_init(struct fb_info *info) OUT_RING(chan, info->fix.line_length); OUT_RING(chan, info->var.xres_virtual); OUT_RING(chan, info->var.yres_virtual); - OUT_RING(chan, 0); - OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys + - dev_priv->vm_vram_base); + OUT_RING(chan, upper_32_bits(fb)); + OUT_RING(chan, lower_32_bits(fb)); BEGIN_RING(chan, NvSub2D, 0x0230, 2); OUT_RING(chan, format); OUT_RING(chan, 1); @@ -258,9 +260,8 @@ nv50_fbcon_accel_init(struct fb_info *info) OUT_RING(chan, info->fix.line_length); OUT_RING(chan, info->var.xres_virtual); OUT_RING(chan, info->var.yres_virtual); - OUT_RING(chan, 0); - OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys + - dev_priv->vm_vram_base); + OUT_RING(chan, upper_32_bits(fb)); + OUT_RING(chan, lower_32_bits(fb)); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index 204a79ff10f4..e20c0e2474f3 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -243,6 +243,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; + unsigned long flags; int ret; NV_DEBUG(dev, "ch%d\n", chan->id); @@ -278,19 +279,21 @@ nv50_fifo_create_context(struct nouveau_channel *chan) return ret; } + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + dev_priv->engine.instmem.prepare_access(dev, true); - nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base); - nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base); nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4); nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4)); - nv_wo32(dev, ramfc, 0x3c/4, 0x00086078); nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff); nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff); nv_wo32(dev, ramfc, 0x40/4, 0x00000000); nv_wo32(dev, ramfc, 0x7c/4, 0x30000001); nv_wo32(dev, ramfc, 0x78/4, 0x00000000); - nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff); + nv_wo32(dev, ramfc, 0x3c/4, 0x403f6078); + nv_wo32(dev, ramfc, 0x50/4, chan->pushbuf_base + + chan->dma.ib_base * 4); + nv_wo32(dev, ramfc, 0x54/4, drm_order(chan->dma.ib_max + 1) << 16); if (!IS_G80) { nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id); @@ -306,10 +309,12 @@ nv50_fifo_create_context(struct nouveau_channel *chan) ret = nv50_fifo_channel_enable(dev, chan->id, false); if (ret) { NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); nouveau_gpuobj_ref_del(dev, &chan->ramfc); return ret; } + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c new file mode 100644 index 000000000000..c61782b314e7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_gpio.c @@ -0,0 +1,76 @@ +/* + * 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 + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_hw.h" + +static int +nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift) +{ + const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; + + if (gpio->line > 32) + return -EINVAL; + + *reg = nv50_gpio_reg[gpio->line >> 3]; + *shift = (gpio->line & 7) << 2; + return 0; +} + +int +nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +{ + struct dcb_gpio_entry *gpio; + uint32_t r, s, v; + + gpio = nouveau_bios_gpio_entry(dev, tag); + if (!gpio) + return -ENOENT; + + if (nv50_gpio_location(gpio, &r, &s)) + return -EINVAL; + + v = nv_rd32(dev, r) >> (s + 2); + return ((v & 1) == (gpio->state[1] & 1)); +} + +int +nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +{ + struct dcb_gpio_entry *gpio; + uint32_t r, s, v; + + gpio = nouveau_bios_gpio_entry(dev, tag); + if (!gpio) + return -ENOENT; + + if (nv50_gpio_location(gpio, &r, &s)) + return -EINVAL; + + v = nv_rd32(dev, r) & ~(0x3 << s); + v |= (gpio->state[state] ^ 2) << s; + nv_wr32(dev, r, v); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index 6d504801b514..b203d06f601f 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -28,30 +28,7 @@ #include "drm.h" #include "nouveau_drv.h" -MODULE_FIRMWARE("nouveau/nv50.ctxprog"); -MODULE_FIRMWARE("nouveau/nv50.ctxvals"); -MODULE_FIRMWARE("nouveau/nv84.ctxprog"); -MODULE_FIRMWARE("nouveau/nv84.ctxvals"); -MODULE_FIRMWARE("nouveau/nv86.ctxprog"); -MODULE_FIRMWARE("nouveau/nv86.ctxvals"); -MODULE_FIRMWARE("nouveau/nv92.ctxprog"); -MODULE_FIRMWARE("nouveau/nv92.ctxvals"); -MODULE_FIRMWARE("nouveau/nv94.ctxprog"); -MODULE_FIRMWARE("nouveau/nv94.ctxvals"); -MODULE_FIRMWARE("nouveau/nv96.ctxprog"); -MODULE_FIRMWARE("nouveau/nv96.ctxvals"); -MODULE_FIRMWARE("nouveau/nv98.ctxprog"); -MODULE_FIRMWARE("nouveau/nv98.ctxvals"); -MODULE_FIRMWARE("nouveau/nva0.ctxprog"); -MODULE_FIRMWARE("nouveau/nva0.ctxvals"); -MODULE_FIRMWARE("nouveau/nva5.ctxprog"); -MODULE_FIRMWARE("nouveau/nva5.ctxvals"); -MODULE_FIRMWARE("nouveau/nva8.ctxprog"); -MODULE_FIRMWARE("nouveau/nva8.ctxvals"); -MODULE_FIRMWARE("nouveau/nvaa.ctxprog"); -MODULE_FIRMWARE("nouveau/nvaa.ctxvals"); -MODULE_FIRMWARE("nouveau/nvac.ctxprog"); -MODULE_FIRMWARE("nouveau/nvac.ctxvals"); +#include "nouveau_grctx.h" #define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50) @@ -79,6 +56,10 @@ nv50_graph_init_intr(struct drm_device *dev) static void nv50_graph_init_regs__nv(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t units = nv_rd32(dev, 0x1540); + int i; + NV_DEBUG(dev, "\n"); nv_wr32(dev, 0x400804, 0xc0000000); @@ -88,6 +69,20 @@ nv50_graph_init_regs__nv(struct drm_device *dev) nv_wr32(dev, 0x405018, 0xc0000000); nv_wr32(dev, 0x402000, 0xc0000000); + for (i = 0; i < 16; i++) { + if (units & 1 << i) { + if (dev_priv->chipset < 0xa0) { + nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000); + nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000); + nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000); + } else { + nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000); + nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000); + nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000); + } + } + } + nv_wr32(dev, 0x400108, 0xffffffff); nv_wr32(dev, 0x400824, 0x00004000); @@ -111,9 +106,34 @@ nv50_graph_init_ctxctl(struct drm_device *dev) NV_DEBUG(dev, "\n"); - nouveau_grctx_prog_load(dev); - if (!dev_priv->engine.graph.ctxprog) - dev_priv->engine.graph.accel_blocked = true; + if (nouveau_ctxfw) { + nouveau_grctx_prog_load(dev); + dev_priv->engine.graph.grctx_size = 0x70000; + } + if (!dev_priv->engine.graph.ctxprog) { + struct nouveau_grctx ctx = {}; + uint32_t *cp = kmalloc(512 * 4, GFP_KERNEL); + int i; + if (!cp) { + NV_ERROR(dev, "Couldn't alloc ctxprog! Disabling acceleration.\n"); + dev_priv->engine.graph.accel_blocked = true; + return 0; + } + ctx.dev = dev; + ctx.mode = NOUVEAU_GRCTX_PROG; + ctx.data = cp; + ctx.ctxprog_max = 512; + if (!nv50_grctx_init(&ctx)) { + dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4; + + nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0); + for (i = 0; i < ctx.ctxprog_len; i++) + nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]); + } else { + dev_priv->engine.graph.accel_blocked = true; + } + kfree(cp); + } nv_wr32(dev, 0x400320, 4); nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0); @@ -193,13 +213,13 @@ nv50_graph_create_context(struct nouveau_channel *chan) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *ramin = chan->ramin->gpuobj; struct nouveau_gpuobj *ctx; - uint32_t grctx_size = 0x70000; + struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; int hdr, ret; NV_DEBUG(dev, "ch%d\n", chan->id); - ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC | + ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx); if (ret) return ret; @@ -209,7 +229,7 @@ nv50_graph_create_context(struct nouveau_channel *chan) dev_priv->engine.instmem.prepare_access(dev, true); nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002); nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance + - grctx_size - 1); + pgraph->grctx_size - 1); nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance); nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0); nv_wo32(dev, ramin, (hdr + 0x10)/4, 0); @@ -217,12 +237,16 @@ nv50_graph_create_context(struct nouveau_channel *chan) dev_priv->engine.instmem.finish_access(dev); dev_priv->engine.instmem.prepare_access(dev, true); - nouveau_grctx_vals_load(dev, ctx); + if (!pgraph->ctxprog) { + struct nouveau_grctx ctx = {}; + ctx.dev = chan->dev; + ctx.mode = NOUVEAU_GRCTX_VALS; + ctx.data = chan->ramin_grctx->gpuobj; + nv50_grctx_init(&ctx); + } else { + nouveau_grctx_vals_load(dev, ctx); + } nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12); - if ((dev_priv->chipset & 0xf0) == 0xa0) - nv_wo32(dev, ctx, 0x00004/4, 0x00000000); - else - nv_wo32(dev, ctx, 0x0011c/4, 0x00000000); dev_priv->engine.instmem.finish_access(dev); return 0; @@ -386,9 +410,10 @@ struct nouveau_pgraph_object_class nv50_graph_grclass[] = { { 0x5039, false, NULL }, /* m2mf */ { 0x502d, false, NULL }, /* 2d */ { 0x50c0, false, NULL }, /* compute */ + { 0x85c0, false, NULL }, /* compute (nva3, nva5, nva8) */ { 0x5097, false, NULL }, /* tesla (nv50) */ - { 0x8297, false, NULL }, /* tesla (nv80/nv90) */ - { 0x8397, false, NULL }, /* tesla (nva0) */ - { 0x8597, false, NULL }, /* tesla (nva8) */ + { 0x8297, false, NULL }, /* tesla (nv8x/nv9x) */ + { 0x8397, false, NULL }, /* tesla (nva0, nvaa, nvac) */ + { 0x8597, false, NULL }, /* tesla (nva3, nva5, nva8) */ {} }; diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c new file mode 100644 index 000000000000..42a8fb20c1e6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_grctx.c @@ -0,0 +1,2383 @@ +/* + * Copyright 2009 Marcin Kościelnicki + * + * 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. + */ + +#define CP_FLAG_CLEAR 0 +#define CP_FLAG_SET 1 +#define CP_FLAG_SWAP_DIRECTION ((0 * 32) + 0) +#define CP_FLAG_SWAP_DIRECTION_LOAD 0 +#define CP_FLAG_SWAP_DIRECTION_SAVE 1 +#define CP_FLAG_UNK01 ((0 * 32) + 1) +#define CP_FLAG_UNK01_CLEAR 0 +#define CP_FLAG_UNK01_SET 1 +#define CP_FLAG_UNK03 ((0 * 32) + 3) +#define CP_FLAG_UNK03_CLEAR 0 +#define CP_FLAG_UNK03_SET 1 +#define CP_FLAG_USER_SAVE ((0 * 32) + 5) +#define CP_FLAG_USER_SAVE_NOT_PENDING 0 +#define CP_FLAG_USER_SAVE_PENDING 1 +#define CP_FLAG_USER_LOAD ((0 * 32) + 6) +#define CP_FLAG_USER_LOAD_NOT_PENDING 0 +#define CP_FLAG_USER_LOAD_PENDING 1 +#define CP_FLAG_UNK0B ((0 * 32) + 0xb) +#define CP_FLAG_UNK0B_CLEAR 0 +#define CP_FLAG_UNK0B_SET 1 +#define CP_FLAG_UNK1D ((0 * 32) + 0x1d) +#define CP_FLAG_UNK1D_CLEAR 0 +#define CP_FLAG_UNK1D_SET 1 +#define CP_FLAG_UNK20 ((1 * 32) + 0) +#define CP_FLAG_UNK20_CLEAR 0 +#define CP_FLAG_UNK20_SET 1 +#define CP_FLAG_STATUS ((2 * 32) + 0) +#define CP_FLAG_STATUS_BUSY 0 +#define CP_FLAG_STATUS_IDLE 1 +#define CP_FLAG_AUTO_SAVE ((2 * 32) + 4) +#define CP_FLAG_AUTO_SAVE_NOT_PENDING 0 +#define CP_FLAG_AUTO_SAVE_PENDING 1 +#define CP_FLAG_AUTO_LOAD ((2 * 32) + 5) +#define CP_FLAG_AUTO_LOAD_NOT_PENDING 0 +#define CP_FLAG_AUTO_LOAD_PENDING 1 +#define CP_FLAG_NEWCTX ((2 * 32) + 10) +#define CP_FLAG_NEWCTX_BUSY 0 +#define CP_FLAG_NEWCTX_DONE 1 +#define CP_FLAG_XFER ((2 * 32) + 11) +#define CP_FLAG_XFER_IDLE 0 +#define CP_FLAG_XFER_BUSY 1 +#define CP_FLAG_ALWAYS ((2 * 32) + 13) +#define CP_FLAG_ALWAYS_FALSE 0 +#define CP_FLAG_ALWAYS_TRUE 1 +#define CP_FLAG_INTR ((2 * 32) + 15) +#define CP_FLAG_INTR_NOT_PENDING 0 +#define CP_FLAG_INTR_PENDING 1 + +#define CP_CTX 0x00100000 +#define CP_CTX_COUNT 0x000f0000 +#define CP_CTX_COUNT_SHIFT 16 +#define CP_CTX_REG 0x00003fff +#define CP_LOAD_SR 0x00200000 +#define CP_LOAD_SR_VALUE 0x000fffff +#define CP_BRA 0x00400000 +#define CP_BRA_IP 0x0001ff00 +#define CP_BRA_IP_SHIFT 8 +#define CP_BRA_IF_CLEAR 0x00000080 +#define CP_BRA_FLAG 0x0000007f +#define CP_WAIT 0x00500000 +#define CP_WAIT_SET 0x00000080 +#define CP_WAIT_FLAG 0x0000007f +#define CP_SET 0x00700000 +#define CP_SET_1 0x00000080 +#define CP_SET_FLAG 0x0000007f +#define CP_NEWCTX 0x00600004 +#define CP_NEXT_TO_SWAP 0x00600005 +#define CP_SET_CONTEXT_POINTER 0x00600006 +#define CP_SET_XFER_POINTER 0x00600007 +#define CP_ENABLE 0x00600009 +#define CP_END 0x0060000c +#define CP_NEXT_TO_CURRENT 0x0060000d +#define CP_DISABLE1 0x0090ffff +#define CP_DISABLE2 0x0091ffff +#define CP_XFER_1 0x008000ff +#define CP_XFER_2 0x008800ff +#define CP_SEEK_1 0x00c000ff +#define CP_SEEK_2 0x00c800ff + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_grctx.h" + +/* + * This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's + * the GPU itself that does context-switching, but it needs a special + * microcode to do it. And it's the driver's task to supply this microcode, + * further known as ctxprog, as well as the initial context values, known + * as ctxvals. + * + * Without ctxprog, you cannot switch contexts. Not even in software, since + * the majority of context [xfer strands] isn't accessible directly. You're + * stuck with a single channel, and you also suffer all the problems resulting + * from missing ctxvals, since you cannot load them. + * + * Without ctxvals, you're stuck with PGRAPH's default context. It's enough to + * run 2d operations, but trying to utilise 3d or CUDA will just lock you up, + * since you don't have... some sort of needed setup. + * + * Nouveau will just disable acceleration if not given ctxprog + ctxvals, since + * it's too much hassle to handle no-ctxprog as a special case. + */ + +/* + * How ctxprogs work. + * + * The ctxprog is written in its own kind of microcode, with very small and + * crappy set of available commands. You upload it to a small [512 insns] + * area of memory on PGRAPH, and it'll be run when PFIFO wants PGRAPH to + * switch channel. or when the driver explicitely requests it. Stuff visible + * to ctxprog consists of: PGRAPH MMIO registers, PGRAPH context strands, + * the per-channel context save area in VRAM [known as ctxvals or grctx], + * 4 flags registers, a scratch register, two grctx pointers, plus many + * random poorly-understood details. + * + * When ctxprog runs, it's supposed to check what operations are asked of it, + * save old context if requested, optionally reset PGRAPH and switch to the + * new channel, and load the new context. Context consists of three major + * parts: subset of MMIO registers and two "xfer areas". + */ + +/* TODO: + * - document unimplemented bits compared to nvidia + * - NVAx: make a TP subroutine, use it. + * - use 0x4008fc instead of 0x1540? + */ + +enum cp_label { + cp_check_load = 1, + cp_setup_auto_load, + cp_setup_load, + cp_setup_save, + cp_swap_state, + cp_prepare_exit, + cp_exit, +}; + +static void nv50_graph_construct_mmio(struct nouveau_grctx *ctx); +static void nv50_graph_construct_xfer1(struct nouveau_grctx *ctx); +static void nv50_graph_construct_xfer2(struct nouveau_grctx *ctx); + +/* Main function: construct the ctxprog skeleton, call the other functions. */ + +int +nv50_grctx_init(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + + switch (dev_priv->chipset) { + case 0x50: + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0xa0: + case 0xa3: + case 0xa5: + case 0xa8: + case 0xaa: + case 0xac: + break; + default: + NV_ERROR(ctx->dev, "I don't know how to make a ctxprog for " + "your NV%x card.\n", dev_priv->chipset); + NV_ERROR(ctx->dev, "Disabling acceleration. Please contact " + "the devs.\n"); + return -ENOSYS; + } + /* decide whether we're loading/unloading the context */ + cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save); + cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save); + + cp_name(ctx, cp_check_load); + cp_bra (ctx, AUTO_LOAD, PENDING, cp_setup_auto_load); + cp_bra (ctx, USER_LOAD, PENDING, cp_setup_load); + cp_bra (ctx, ALWAYS, TRUE, cp_exit); + + /* setup for context load */ + cp_name(ctx, cp_setup_auto_load); + cp_out (ctx, CP_DISABLE1); + cp_out (ctx, CP_DISABLE2); + cp_out (ctx, CP_ENABLE); + cp_out (ctx, CP_NEXT_TO_SWAP); + cp_set (ctx, UNK01, SET); + cp_name(ctx, cp_setup_load); + cp_out (ctx, CP_NEWCTX); + cp_wait(ctx, NEWCTX, BUSY); + cp_set (ctx, UNK1D, CLEAR); + cp_set (ctx, SWAP_DIRECTION, LOAD); + cp_bra (ctx, UNK0B, SET, cp_prepare_exit); + cp_bra (ctx, ALWAYS, TRUE, cp_swap_state); + + /* setup for context save */ + cp_name(ctx, cp_setup_save); + cp_set (ctx, UNK1D, SET); + cp_wait(ctx, STATUS, BUSY); + cp_wait(ctx, INTR, PENDING); + cp_bra (ctx, STATUS, BUSY, cp_setup_save); + cp_set (ctx, UNK01, SET); + cp_set (ctx, SWAP_DIRECTION, SAVE); + + /* general PGRAPH state */ + cp_name(ctx, cp_swap_state); + cp_set (ctx, UNK03, SET); + cp_pos (ctx, 0x00004/4); + cp_ctx (ctx, 0x400828, 1); /* needed. otherwise, flickering happens. */ + cp_pos (ctx, 0x00100/4); + nv50_graph_construct_mmio(ctx); + nv50_graph_construct_xfer1(ctx); + nv50_graph_construct_xfer2(ctx); + + cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_check_load); + + cp_set (ctx, UNK20, SET); + cp_set (ctx, SWAP_DIRECTION, SAVE); /* no idea why this is needed, but fixes at least one lockup. */ + cp_lsr (ctx, ctx->ctxvals_base); + cp_out (ctx, CP_SET_XFER_POINTER); + cp_lsr (ctx, 4); + cp_out (ctx, CP_SEEK_1); + cp_out (ctx, CP_XFER_1); + cp_wait(ctx, XFER, BUSY); + + /* pre-exit state updates */ + cp_name(ctx, cp_prepare_exit); + cp_set (ctx, UNK01, CLEAR); + cp_set (ctx, UNK03, CLEAR); + cp_set (ctx, UNK1D, CLEAR); + + cp_bra (ctx, USER_SAVE, PENDING, cp_exit); + cp_out (ctx, CP_NEXT_TO_CURRENT); + + cp_name(ctx, cp_exit); + cp_set (ctx, USER_SAVE, NOT_PENDING); + cp_set (ctx, USER_LOAD, NOT_PENDING); + cp_out (ctx, CP_END); + ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */ + + return 0; +} + +/* + * Constructs MMIO part of ctxprog and ctxvals. Just a matter of knowing which + * registers to save/restore and the default values for them. + */ + +static void +nv50_graph_construct_mmio(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int i, j; + int offset, base; + uint32_t units = nv_rd32 (ctx->dev, 0x1540); + + /* 0800: DISPATCH */ + cp_ctx(ctx, 0x400808, 7); + gr_def(ctx, 0x400814, 0x00000030); + cp_ctx(ctx, 0x400834, 0x32); + if (dev_priv->chipset == 0x50) { + gr_def(ctx, 0x400834, 0xff400040); + gr_def(ctx, 0x400838, 0xfff00080); + gr_def(ctx, 0x40083c, 0xfff70090); + gr_def(ctx, 0x400840, 0xffe806a8); + } + gr_def(ctx, 0x400844, 0x00000002); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + gr_def(ctx, 0x400894, 0x00001000); + gr_def(ctx, 0x4008e8, 0x00000003); + gr_def(ctx, 0x4008ec, 0x00001000); + if (dev_priv->chipset == 0x50) + cp_ctx(ctx, 0x400908, 0xb); + else if (dev_priv->chipset < 0xa0) + cp_ctx(ctx, 0x400908, 0xc); + else + cp_ctx(ctx, 0x400908, 0xe); + + if (dev_priv->chipset >= 0xa0) + cp_ctx(ctx, 0x400b00, 0x1); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + cp_ctx(ctx, 0x400b10, 0x1); + gr_def(ctx, 0x400b10, 0x0001629d); + cp_ctx(ctx, 0x400b20, 0x1); + gr_def(ctx, 0x400b20, 0x0001629d); + } + + /* 0C00: VFETCH */ + cp_ctx(ctx, 0x400c08, 0x2); + gr_def(ctx, 0x400c08, 0x0000fe0c); + + /* 1000 */ + if (dev_priv->chipset < 0xa0) { + cp_ctx(ctx, 0x401008, 0x4); + gr_def(ctx, 0x401014, 0x00001000); + } else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) { + cp_ctx(ctx, 0x401008, 0x5); + gr_def(ctx, 0x401018, 0x00001000); + } else { + cp_ctx(ctx, 0x401008, 0x5); + gr_def(ctx, 0x401018, 0x00004000); + } + + /* 1400 */ + cp_ctx(ctx, 0x401400, 0x8); + cp_ctx(ctx, 0x401424, 0x3); + if (dev_priv->chipset == 0x50) + gr_def(ctx, 0x40142c, 0x0001fd87); + else + gr_def(ctx, 0x40142c, 0x00000187); + cp_ctx(ctx, 0x401540, 0x5); + gr_def(ctx, 0x401550, 0x00001018); + + /* 1800: STREAMOUT */ + cp_ctx(ctx, 0x401814, 0x1); + gr_def(ctx, 0x401814, 0x000000ff); + if (dev_priv->chipset == 0x50) { + cp_ctx(ctx, 0x40181c, 0xe); + gr_def(ctx, 0x401850, 0x00000004); + } else if (dev_priv->chipset < 0xa0) { + cp_ctx(ctx, 0x40181c, 0xf); + gr_def(ctx, 0x401854, 0x00000004); + } else { + cp_ctx(ctx, 0x40181c, 0x13); + gr_def(ctx, 0x401864, 0x00000004); + } + + /* 1C00 */ + cp_ctx(ctx, 0x401c00, 0x1); + switch (dev_priv->chipset) { + case 0x50: + gr_def(ctx, 0x401c00, 0x0001005f); + break; + case 0x84: + case 0x86: + case 0x94: + gr_def(ctx, 0x401c00, 0x044d00df); + break; + case 0x92: + case 0x96: + case 0x98: + case 0xa0: + case 0xaa: + case 0xac: + gr_def(ctx, 0x401c00, 0x042500df); + break; + case 0xa3: + case 0xa5: + case 0xa8: + gr_def(ctx, 0x401c00, 0x142500df); + break; + } + + /* 2400 */ + cp_ctx(ctx, 0x402400, 0x1); + if (dev_priv->chipset == 0x50) + cp_ctx(ctx, 0x402408, 0x1); + else + cp_ctx(ctx, 0x402408, 0x2); + gr_def(ctx, 0x402408, 0x00000600); + + /* 2800 */ + cp_ctx(ctx, 0x402800, 0x1); + if (dev_priv->chipset == 0x50) + gr_def(ctx, 0x402800, 0x00000006); + + /* 2C00 */ + cp_ctx(ctx, 0x402c08, 0x6); + if (dev_priv->chipset != 0x50) + gr_def(ctx, 0x402c14, 0x01000000); + gr_def(ctx, 0x402c18, 0x000000ff); + if (dev_priv->chipset == 0x50) + cp_ctx(ctx, 0x402ca0, 0x1); + else + cp_ctx(ctx, 0x402ca0, 0x2); + if (dev_priv->chipset < 0xa0) + gr_def(ctx, 0x402ca0, 0x00000400); + else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) + gr_def(ctx, 0x402ca0, 0x00000800); + else + gr_def(ctx, 0x402ca0, 0x00000400); + cp_ctx(ctx, 0x402cac, 0x4); + + /* 3000 */ + cp_ctx(ctx, 0x403004, 0x1); + gr_def(ctx, 0x403004, 0x00000001); + + /* 3404 */ + if (dev_priv->chipset >= 0xa0) { + cp_ctx(ctx, 0x403404, 0x1); + gr_def(ctx, 0x403404, 0x00000001); + } + + /* 5000 */ + cp_ctx(ctx, 0x405000, 0x1); + switch (dev_priv->chipset) { + case 0x50: + gr_def(ctx, 0x405000, 0x00300080); + break; + case 0x84: + case 0xa0: + case 0xa3: + case 0xa5: + case 0xa8: + case 0xaa: + case 0xac: + gr_def(ctx, 0x405000, 0x000e0080); + break; + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + gr_def(ctx, 0x405000, 0x00000080); + break; + } + cp_ctx(ctx, 0x405014, 0x1); + gr_def(ctx, 0x405014, 0x00000004); + cp_ctx(ctx, 0x40501c, 0x1); + cp_ctx(ctx, 0x405024, 0x1); + cp_ctx(ctx, 0x40502c, 0x1); + + /* 5400 or maybe 4800 */ + if (dev_priv->chipset == 0x50) { + offset = 0x405400; + cp_ctx(ctx, 0x405400, 0xea); + } else if (dev_priv->chipset < 0x94) { + offset = 0x405400; + cp_ctx(ctx, 0x405400, 0xcb); + } else if (dev_priv->chipset < 0xa0) { + offset = 0x405400; + cp_ctx(ctx, 0x405400, 0xcc); + } else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + offset = 0x404800; + cp_ctx(ctx, 0x404800, 0xda); + } else { + offset = 0x405400; + cp_ctx(ctx, 0x405400, 0xd4); + } + gr_def(ctx, offset + 0x0c, 0x00000002); + gr_def(ctx, offset + 0x10, 0x00000001); + if (dev_priv->chipset >= 0x94) + offset += 4; + gr_def(ctx, offset + 0x1c, 0x00000001); + gr_def(ctx, offset + 0x20, 0x00000100); + gr_def(ctx, offset + 0x38, 0x00000002); + gr_def(ctx, offset + 0x3c, 0x00000001); + gr_def(ctx, offset + 0x40, 0x00000001); + gr_def(ctx, offset + 0x50, 0x00000001); + gr_def(ctx, offset + 0x54, 0x003fffff); + gr_def(ctx, offset + 0x58, 0x00001fff); + gr_def(ctx, offset + 0x60, 0x00000001); + gr_def(ctx, offset + 0x64, 0x00000001); + gr_def(ctx, offset + 0x6c, 0x00000001); + gr_def(ctx, offset + 0x70, 0x00000001); + gr_def(ctx, offset + 0x74, 0x00000001); + gr_def(ctx, offset + 0x78, 0x00000004); + gr_def(ctx, offset + 0x7c, 0x00000001); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + offset += 4; + gr_def(ctx, offset + 0x80, 0x00000001); + gr_def(ctx, offset + 0x84, 0x00000001); + gr_def(ctx, offset + 0x88, 0x00000007); + gr_def(ctx, offset + 0x8c, 0x00000001); + gr_def(ctx, offset + 0x90, 0x00000007); + gr_def(ctx, offset + 0x94, 0x00000001); + gr_def(ctx, offset + 0x98, 0x00000001); + gr_def(ctx, offset + 0x9c, 0x00000001); + if (dev_priv->chipset == 0x50) { + gr_def(ctx, offset + 0xb0, 0x00000001); + gr_def(ctx, offset + 0xb4, 0x00000001); + gr_def(ctx, offset + 0xbc, 0x00000001); + gr_def(ctx, offset + 0xc0, 0x0000000a); + gr_def(ctx, offset + 0xd0, 0x00000040); + gr_def(ctx, offset + 0xd8, 0x00000002); + gr_def(ctx, offset + 0xdc, 0x00000100); + gr_def(ctx, offset + 0xe0, 0x00000001); + gr_def(ctx, offset + 0xe4, 0x00000100); + gr_def(ctx, offset + 0x100, 0x00000001); + gr_def(ctx, offset + 0x124, 0x00000004); + gr_def(ctx, offset + 0x13c, 0x00000001); + gr_def(ctx, offset + 0x140, 0x00000100); + gr_def(ctx, offset + 0x148, 0x00000001); + gr_def(ctx, offset + 0x154, 0x00000100); + gr_def(ctx, offset + 0x158, 0x00000001); + gr_def(ctx, offset + 0x15c, 0x00000100); + gr_def(ctx, offset + 0x164, 0x00000001); + gr_def(ctx, offset + 0x170, 0x00000100); + gr_def(ctx, offset + 0x174, 0x00000001); + gr_def(ctx, offset + 0x17c, 0x00000001); + gr_def(ctx, offset + 0x188, 0x00000002); + gr_def(ctx, offset + 0x190, 0x00000001); + gr_def(ctx, offset + 0x198, 0x00000001); + gr_def(ctx, offset + 0x1ac, 0x00000003); + offset += 0xd0; + } else { + gr_def(ctx, offset + 0xb0, 0x00000001); + gr_def(ctx, offset + 0xb4, 0x00000100); + gr_def(ctx, offset + 0xbc, 0x00000001); + gr_def(ctx, offset + 0xc8, 0x00000100); + gr_def(ctx, offset + 0xcc, 0x00000001); + gr_def(ctx, offset + 0xd0, 0x00000100); + gr_def(ctx, offset + 0xd8, 0x00000001); + gr_def(ctx, offset + 0xe4, 0x00000100); + } + gr_def(ctx, offset + 0xf8, 0x00000004); + gr_def(ctx, offset + 0xfc, 0x00000070); + gr_def(ctx, offset + 0x100, 0x00000080); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + offset += 4; + gr_def(ctx, offset + 0x114, 0x0000000c); + if (dev_priv->chipset == 0x50) + offset -= 4; + gr_def(ctx, offset + 0x11c, 0x00000008); + gr_def(ctx, offset + 0x120, 0x00000014); + if (dev_priv->chipset == 0x50) { + gr_def(ctx, offset + 0x124, 0x00000026); + offset -= 0x18; + } else { + gr_def(ctx, offset + 0x128, 0x00000029); + gr_def(ctx, offset + 0x12c, 0x00000027); + gr_def(ctx, offset + 0x130, 0x00000026); + gr_def(ctx, offset + 0x134, 0x00000008); + gr_def(ctx, offset + 0x138, 0x00000004); + gr_def(ctx, offset + 0x13c, 0x00000027); + } + gr_def(ctx, offset + 0x148, 0x00000001); + gr_def(ctx, offset + 0x14c, 0x00000002); + gr_def(ctx, offset + 0x150, 0x00000003); + gr_def(ctx, offset + 0x154, 0x00000004); + gr_def(ctx, offset + 0x158, 0x00000005); + gr_def(ctx, offset + 0x15c, 0x00000006); + gr_def(ctx, offset + 0x160, 0x00000007); + gr_def(ctx, offset + 0x164, 0x00000001); + gr_def(ctx, offset + 0x1a8, 0x000000cf); + if (dev_priv->chipset == 0x50) + offset -= 4; + gr_def(ctx, offset + 0x1d8, 0x00000080); + gr_def(ctx, offset + 0x1dc, 0x00000004); + gr_def(ctx, offset + 0x1e0, 0x00000004); + if (dev_priv->chipset == 0x50) + offset -= 4; + else + gr_def(ctx, offset + 0x1e4, 0x00000003); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + gr_def(ctx, offset + 0x1ec, 0x00000003); + offset += 8; + } + gr_def(ctx, offset + 0x1e8, 0x00000001); + if (dev_priv->chipset == 0x50) + offset -= 4; + gr_def(ctx, offset + 0x1f4, 0x00000012); + gr_def(ctx, offset + 0x1f8, 0x00000010); + gr_def(ctx, offset + 0x1fc, 0x0000000c); + gr_def(ctx, offset + 0x200, 0x00000001); + gr_def(ctx, offset + 0x210, 0x00000004); + gr_def(ctx, offset + 0x214, 0x00000002); + gr_def(ctx, offset + 0x218, 0x00000004); + if (dev_priv->chipset >= 0xa0) + offset += 4; + gr_def(ctx, offset + 0x224, 0x003fffff); + gr_def(ctx, offset + 0x228, 0x00001fff); + if (dev_priv->chipset == 0x50) + offset -= 0x20; + else if (dev_priv->chipset >= 0xa0) { + gr_def(ctx, offset + 0x250, 0x00000001); + gr_def(ctx, offset + 0x254, 0x00000001); + gr_def(ctx, offset + 0x258, 0x00000002); + offset += 0x10; + } + gr_def(ctx, offset + 0x250, 0x00000004); + gr_def(ctx, offset + 0x254, 0x00000014); + gr_def(ctx, offset + 0x258, 0x00000001); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + offset += 4; + gr_def(ctx, offset + 0x264, 0x00000002); + if (dev_priv->chipset >= 0xa0) + offset += 8; + gr_def(ctx, offset + 0x270, 0x00000001); + gr_def(ctx, offset + 0x278, 0x00000002); + gr_def(ctx, offset + 0x27c, 0x00001000); + if (dev_priv->chipset == 0x50) + offset -= 0xc; + else { + gr_def(ctx, offset + 0x280, 0x00000e00); + gr_def(ctx, offset + 0x284, 0x00001000); + gr_def(ctx, offset + 0x288, 0x00001e00); + } + gr_def(ctx, offset + 0x290, 0x00000001); + gr_def(ctx, offset + 0x294, 0x00000001); + gr_def(ctx, offset + 0x298, 0x00000001); + gr_def(ctx, offset + 0x29c, 0x00000001); + gr_def(ctx, offset + 0x2a0, 0x00000001); + gr_def(ctx, offset + 0x2b0, 0x00000200); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + gr_def(ctx, offset + 0x2b4, 0x00000200); + offset += 4; + } + if (dev_priv->chipset < 0xa0) { + gr_def(ctx, offset + 0x2b8, 0x00000001); + gr_def(ctx, offset + 0x2bc, 0x00000070); + gr_def(ctx, offset + 0x2c0, 0x00000080); + gr_def(ctx, offset + 0x2cc, 0x00000001); + gr_def(ctx, offset + 0x2d0, 0x00000070); + gr_def(ctx, offset + 0x2d4, 0x00000080); + } else { + gr_def(ctx, offset + 0x2b8, 0x00000001); + gr_def(ctx, offset + 0x2bc, 0x000000f0); + gr_def(ctx, offset + 0x2c0, 0x000000ff); + gr_def(ctx, offset + 0x2cc, 0x00000001); + gr_def(ctx, offset + 0x2d0, 0x000000f0); + gr_def(ctx, offset + 0x2d4, 0x000000ff); + gr_def(ctx, offset + 0x2dc, 0x00000009); + offset += 4; + } + gr_def(ctx, offset + 0x2e4, 0x00000001); + gr_def(ctx, offset + 0x2e8, 0x000000cf); + gr_def(ctx, offset + 0x2f0, 0x00000001); + gr_def(ctx, offset + 0x300, 0x000000cf); + gr_def(ctx, offset + 0x308, 0x00000002); + gr_def(ctx, offset + 0x310, 0x00000001); + gr_def(ctx, offset + 0x318, 0x00000001); + gr_def(ctx, offset + 0x320, 0x000000cf); + gr_def(ctx, offset + 0x324, 0x000000cf); + gr_def(ctx, offset + 0x328, 0x00000001); + + /* 6000? */ + if (dev_priv->chipset == 0x50) + cp_ctx(ctx, 0x4063e0, 0x1); + + /* 6800: M2MF */ + if (dev_priv->chipset < 0x90) { + cp_ctx(ctx, 0x406814, 0x2b); + gr_def(ctx, 0x406818, 0x00000f80); + gr_def(ctx, 0x406860, 0x007f0080); + gr_def(ctx, 0x40689c, 0x007f0080); + } else { + cp_ctx(ctx, 0x406814, 0x4); + if (dev_priv->chipset == 0x98) + gr_def(ctx, 0x406818, 0x00000f80); + else + gr_def(ctx, 0x406818, 0x00001f80); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + gr_def(ctx, 0x40681c, 0x00000030); + cp_ctx(ctx, 0x406830, 0x3); + } + + /* 7000: per-ROP group state */ + for (i = 0; i < 8; i++) { + if (units & (1<<(i+16))) { + cp_ctx(ctx, 0x407000 + (i<<8), 3); + if (dev_priv->chipset == 0x50) + gr_def(ctx, 0x407000 + (i<<8), 0x1b74f820); + else if (dev_priv->chipset != 0xa5) + gr_def(ctx, 0x407000 + (i<<8), 0x3b74f821); + else + gr_def(ctx, 0x407000 + (i<<8), 0x7b74f821); + gr_def(ctx, 0x407004 + (i<<8), 0x89058001); + + if (dev_priv->chipset == 0x50) { + cp_ctx(ctx, 0x407010 + (i<<8), 1); + } else if (dev_priv->chipset < 0xa0) { + cp_ctx(ctx, 0x407010 + (i<<8), 2); + gr_def(ctx, 0x407010 + (i<<8), 0x00001000); + gr_def(ctx, 0x407014 + (i<<8), 0x0000001f); + } else { + cp_ctx(ctx, 0x407010 + (i<<8), 3); + gr_def(ctx, 0x407010 + (i<<8), 0x00001000); + if (dev_priv->chipset != 0xa5) + gr_def(ctx, 0x407014 + (i<<8), 0x000000ff); + else + gr_def(ctx, 0x407014 + (i<<8), 0x000001ff); + } + + cp_ctx(ctx, 0x407080 + (i<<8), 4); + if (dev_priv->chipset != 0xa5) + gr_def(ctx, 0x407080 + (i<<8), 0x027c10fa); + else + gr_def(ctx, 0x407080 + (i<<8), 0x827c10fa); + if (dev_priv->chipset == 0x50) + gr_def(ctx, 0x407084 + (i<<8), 0x000000c0); + else + gr_def(ctx, 0x407084 + (i<<8), 0x400000c0); + gr_def(ctx, 0x407088 + (i<<8), 0xb7892080); + + if (dev_priv->chipset < 0xa0) + cp_ctx(ctx, 0x407094 + (i<<8), 1); + else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) + cp_ctx(ctx, 0x407094 + (i<<8), 3); + else { + cp_ctx(ctx, 0x407094 + (i<<8), 4); + gr_def(ctx, 0x4070a0 + (i<<8), 1); + } + } + } + + cp_ctx(ctx, 0x407c00, 0x3); + if (dev_priv->chipset < 0x90) + gr_def(ctx, 0x407c00, 0x00010040); + else if (dev_priv->chipset < 0xa0) + gr_def(ctx, 0x407c00, 0x00390040); + else + gr_def(ctx, 0x407c00, 0x003d0040); + gr_def(ctx, 0x407c08, 0x00000022); + if (dev_priv->chipset >= 0xa0) { + cp_ctx(ctx, 0x407c10, 0x3); + cp_ctx(ctx, 0x407c20, 0x1); + cp_ctx(ctx, 0x407c2c, 0x1); + } + + if (dev_priv->chipset < 0xa0) { + cp_ctx(ctx, 0x407d00, 0x9); + } else { + cp_ctx(ctx, 0x407d00, 0x15); + } + if (dev_priv->chipset == 0x98) + gr_def(ctx, 0x407d08, 0x00380040); + else { + if (dev_priv->chipset < 0x90) + gr_def(ctx, 0x407d08, 0x00010040); + else if (dev_priv->chipset < 0xa0) + gr_def(ctx, 0x407d08, 0x00390040); + else + gr_def(ctx, 0x407d08, 0x003d0040); + gr_def(ctx, 0x407d0c, 0x00000022); + } + + /* 8000+: per-TP state */ + for (i = 0; i < 10; i++) { + if (units & (1<<i)) { + if (dev_priv->chipset < 0xa0) + base = 0x408000 + (i<<12); + else + base = 0x408000 + (i<<11); + if (dev_priv->chipset < 0xa0) + offset = base + 0xc00; + else + offset = base + 0x80; + cp_ctx(ctx, offset + 0x00, 1); + gr_def(ctx, offset + 0x00, 0x0000ff0a); + cp_ctx(ctx, offset + 0x08, 1); + + /* per-MP state */ + for (j = 0; j < (dev_priv->chipset < 0xa0 ? 2 : 4); j++) { + if (!(units & (1 << (j+24)))) continue; + if (dev_priv->chipset < 0xa0) + offset = base + 0x200 + (j<<7); + else + offset = base + 0x100 + (j<<7); + cp_ctx(ctx, offset, 0x20); + gr_def(ctx, offset + 0x00, 0x01800000); + gr_def(ctx, offset + 0x04, 0x00160000); + gr_def(ctx, offset + 0x08, 0x01800000); + gr_def(ctx, offset + 0x18, 0x0003ffff); + switch (dev_priv->chipset) { + case 0x50: + gr_def(ctx, offset + 0x1c, 0x00080000); + break; + case 0x84: + gr_def(ctx, offset + 0x1c, 0x00880000); + break; + case 0x86: + gr_def(ctx, offset + 0x1c, 0x008c0000); + break; + case 0x92: + case 0x96: + case 0x98: + gr_def(ctx, offset + 0x1c, 0x118c0000); + break; + case 0x94: + gr_def(ctx, offset + 0x1c, 0x10880000); + break; + case 0xa0: + case 0xa5: + gr_def(ctx, offset + 0x1c, 0x310c0000); + break; + case 0xa3: + case 0xa8: + case 0xaa: + case 0xac: + gr_def(ctx, offset + 0x1c, 0x300c0000); + break; + } + gr_def(ctx, offset + 0x40, 0x00010401); + if (dev_priv->chipset == 0x50) + gr_def(ctx, offset + 0x48, 0x00000040); + else + gr_def(ctx, offset + 0x48, 0x00000078); + gr_def(ctx, offset + 0x50, 0x000000bf); + gr_def(ctx, offset + 0x58, 0x00001210); + if (dev_priv->chipset == 0x50) + gr_def(ctx, offset + 0x5c, 0x00000080); + else + gr_def(ctx, offset + 0x5c, 0x08000080); + if (dev_priv->chipset >= 0xa0) + gr_def(ctx, offset + 0x68, 0x0000003e); + } + + if (dev_priv->chipset < 0xa0) + cp_ctx(ctx, base + 0x300, 0x4); + else + cp_ctx(ctx, base + 0x300, 0x5); + if (dev_priv->chipset == 0x50) + gr_def(ctx, base + 0x304, 0x00007070); + else if (dev_priv->chipset < 0xa0) + gr_def(ctx, base + 0x304, 0x00027070); + else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) + gr_def(ctx, base + 0x304, 0x01127070); + else + gr_def(ctx, base + 0x304, 0x05127070); + + if (dev_priv->chipset < 0xa0) + cp_ctx(ctx, base + 0x318, 1); + else + cp_ctx(ctx, base + 0x320, 1); + if (dev_priv->chipset == 0x50) + gr_def(ctx, base + 0x318, 0x0003ffff); + else if (dev_priv->chipset < 0xa0) + gr_def(ctx, base + 0x318, 0x03ffffff); + else + gr_def(ctx, base + 0x320, 0x07ffffff); + + if (dev_priv->chipset < 0xa0) + cp_ctx(ctx, base + 0x324, 5); + else + cp_ctx(ctx, base + 0x328, 4); + + if (dev_priv->chipset < 0xa0) { + cp_ctx(ctx, base + 0x340, 9); + offset = base + 0x340; + } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) { + cp_ctx(ctx, base + 0x33c, 0xb); + offset = base + 0x344; + } else { + cp_ctx(ctx, base + 0x33c, 0xd); + offset = base + 0x344; + } + gr_def(ctx, offset + 0x0, 0x00120407); + gr_def(ctx, offset + 0x4, 0x05091507); + if (dev_priv->chipset == 0x84) + gr_def(ctx, offset + 0x8, 0x05100202); + else + gr_def(ctx, offset + 0x8, 0x05010202); + gr_def(ctx, offset + 0xc, 0x00030201); + if (dev_priv->chipset == 0xa3) + cp_ctx(ctx, base + 0x36c, 1); + + cp_ctx(ctx, base + 0x400, 2); + gr_def(ctx, base + 0x404, 0x00000040); + cp_ctx(ctx, base + 0x40c, 2); + gr_def(ctx, base + 0x40c, 0x0d0c0b0a); + gr_def(ctx, base + 0x410, 0x00141210); + + if (dev_priv->chipset < 0xa0) + offset = base + 0x800; + else + offset = base + 0x500; + cp_ctx(ctx, offset, 6); + gr_def(ctx, offset + 0x0, 0x000001f0); + gr_def(ctx, offset + 0x4, 0x00000001); + gr_def(ctx, offset + 0x8, 0x00000003); + if (dev_priv->chipset == 0x50 || dev_priv->chipset >= 0xaa) + gr_def(ctx, offset + 0xc, 0x00008000); + gr_def(ctx, offset + 0x14, 0x00039e00); + cp_ctx(ctx, offset + 0x1c, 2); + if (dev_priv->chipset == 0x50) + gr_def(ctx, offset + 0x1c, 0x00000040); + else + gr_def(ctx, offset + 0x1c, 0x00000100); + gr_def(ctx, offset + 0x20, 0x00003800); + + if (dev_priv->chipset >= 0xa0) { + cp_ctx(ctx, base + 0x54c, 2); + if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) + gr_def(ctx, base + 0x54c, 0x003fe006); + else + gr_def(ctx, base + 0x54c, 0x003fe007); + gr_def(ctx, base + 0x550, 0x003fe000); + } + + if (dev_priv->chipset < 0xa0) + offset = base + 0xa00; + else + offset = base + 0x680; + cp_ctx(ctx, offset, 1); + gr_def(ctx, offset, 0x00404040); + + if (dev_priv->chipset < 0xa0) + offset = base + 0xe00; + else + offset = base + 0x700; + cp_ctx(ctx, offset, 2); + if (dev_priv->chipset < 0xa0) + gr_def(ctx, offset, 0x0077f005); + else if (dev_priv->chipset == 0xa5) + gr_def(ctx, offset, 0x6cf7f007); + else if (dev_priv->chipset == 0xa8) + gr_def(ctx, offset, 0x6cfff007); + else if (dev_priv->chipset == 0xac) + gr_def(ctx, offset, 0x0cfff007); + else + gr_def(ctx, offset, 0x0cf7f007); + if (dev_priv->chipset == 0x50) + gr_def(ctx, offset + 0x4, 0x00007fff); + else if (dev_priv->chipset < 0xa0) + gr_def(ctx, offset + 0x4, 0x003f7fff); + else + gr_def(ctx, offset + 0x4, 0x02bf7fff); + cp_ctx(ctx, offset + 0x2c, 1); + if (dev_priv->chipset == 0x50) { + cp_ctx(ctx, offset + 0x50, 9); + gr_def(ctx, offset + 0x54, 0x000003ff); + gr_def(ctx, offset + 0x58, 0x00000003); + gr_def(ctx, offset + 0x5c, 0x00000003); + gr_def(ctx, offset + 0x60, 0x000001ff); + gr_def(ctx, offset + 0x64, 0x0000001f); + gr_def(ctx, offset + 0x68, 0x0000000f); + gr_def(ctx, offset + 0x6c, 0x0000000f); + } else if(dev_priv->chipset < 0xa0) { + cp_ctx(ctx, offset + 0x50, 1); + cp_ctx(ctx, offset + 0x70, 1); + } else { + cp_ctx(ctx, offset + 0x50, 1); + cp_ctx(ctx, offset + 0x60, 5); + } + } + } +} + +/* + * xfer areas. These are a pain. + * + * There are 2 xfer areas: the first one is big and contains all sorts of + * stuff, the second is small and contains some per-TP context. + * + * Each area is split into 8 "strands". The areas, when saved to grctx, + * are made of 8-word blocks. Each block contains a single word from + * each strand. The strands are independent of each other, their + * addresses are unrelated to each other, and data in them is closely + * packed together. The strand layout varies a bit between cards: here + * and there, a single word is thrown out in the middle and the whole + * strand is offset by a bit from corresponding one on another chipset. + * For this reason, addresses of stuff in strands are almost useless. + * Knowing sequence of stuff and size of gaps between them is much more + * useful, and that's how we build the strands in our generator. + * + * NVA0 takes this mess to a whole new level by cutting the old strands + * into a few dozen pieces [known as genes], rearranging them randomly, + * and putting them back together to make new strands. Hopefully these + * genes correspond more or less directly to the same PGRAPH subunits + * as in 400040 register. + * + * The most common value in default context is 0, and when the genes + * are separated by 0's, gene bounduaries are quite speculative... + * some of them can be clearly deduced, others can be guessed, and yet + * others won't be resolved without figuring out the real meaning of + * given ctxval. For the same reason, ending point of each strand + * is unknown. Except for strand 0, which is the longest strand and + * its end corresponds to end of the whole xfer. + * + * An unsolved mystery is the seek instruction: it takes an argument + * in bits 8-18, and that argument is clearly the place in strands to + * seek to... but the offsets don't seem to correspond to offsets as + * seen in grctx. Perhaps there's another, real, not randomly-changing + * addressing in strands, and the xfer insn just happens to skip over + * the unused bits? NV10-NV30 PIPE comes to mind... + * + * As far as I know, there's no way to access the xfer areas directly + * without the help of ctxprog. + */ + +static inline void +xf_emit(struct nouveau_grctx *ctx, int num, uint32_t val) { + int i; + if (val && ctx->mode == NOUVEAU_GRCTX_VALS) + for (i = 0; i < num; i++) + nv_wo32(ctx->dev, ctx->data, ctx->ctxvals_pos + (i << 3), val); + ctx->ctxvals_pos += num << 3; +} + +/* Gene declarations... */ + +static void nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx); +static void nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx); +static void nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx); + +static void +nv50_graph_construct_xfer1(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int i; + int offset; + int size = 0; + uint32_t units = nv_rd32 (ctx->dev, 0x1540); + + offset = (ctx->ctxvals_pos+0x3f)&~0x3f; + ctx->ctxvals_base = offset; + + if (dev_priv->chipset < 0xa0) { + /* Strand 0 */ + ctx->ctxvals_pos = offset; + switch (dev_priv->chipset) { + case 0x50: + xf_emit(ctx, 0x99, 0); + break; + case 0x84: + case 0x86: + xf_emit(ctx, 0x384, 0); + break; + case 0x92: + case 0x94: + case 0x96: + case 0x98: + xf_emit(ctx, 0x380, 0); + break; + } + nv50_graph_construct_gene_m2mf (ctx); + switch (dev_priv->chipset) { + case 0x50: + case 0x84: + case 0x86: + case 0x98: + xf_emit(ctx, 0x4c4, 0); + break; + case 0x92: + case 0x94: + case 0x96: + xf_emit(ctx, 0x984, 0); + break; + } + nv50_graph_construct_gene_unk5(ctx); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 0xa, 0); + else + xf_emit(ctx, 0xb, 0); + nv50_graph_construct_gene_unk4(ctx); + nv50_graph_construct_gene_unk3(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 1 */ + ctx->ctxvals_pos = offset + 0x1; + nv50_graph_construct_gene_unk6(ctx); + nv50_graph_construct_gene_unk7(ctx); + nv50_graph_construct_gene_unk8(ctx); + switch (dev_priv->chipset) { + case 0x50: + case 0x92: + xf_emit(ctx, 0xfb, 0); + break; + case 0x84: + xf_emit(ctx, 0xd3, 0); + break; + case 0x94: + case 0x96: + xf_emit(ctx, 0xab, 0); + break; + case 0x86: + case 0x98: + xf_emit(ctx, 0x6b, 0); + break; + } + xf_emit(ctx, 2, 0x4e3bfdf); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 0xb, 0); + xf_emit(ctx, 2, 0x4e3bfdf); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 2 */ + ctx->ctxvals_pos = offset + 0x2; + switch (dev_priv->chipset) { + case 0x50: + case 0x92: + xf_emit(ctx, 0xa80, 0); + break; + case 0x84: + xf_emit(ctx, 0xa7e, 0); + break; + case 0x94: + case 0x96: + xf_emit(ctx, 0xa7c, 0); + break; + case 0x86: + case 0x98: + xf_emit(ctx, 0xa7a, 0); + break; + } + xf_emit(ctx, 1, 0x3fffff); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x1fff); + xf_emit(ctx, 0xe, 0); + nv50_graph_construct_gene_unk9(ctx); + nv50_graph_construct_gene_unk2(ctx); + nv50_graph_construct_gene_unk1(ctx); + nv50_graph_construct_gene_unk10(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 3: per-ROP group state */ + ctx->ctxvals_pos = offset + 3; + for (i = 0; i < 6; i++) + if (units & (1 << (i + 16))) + nv50_graph_construct_gene_ropc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strands 4-7: per-TP state */ + for (i = 0; i < 4; i++) { + ctx->ctxvals_pos = offset + 4 + i; + if (units & (1 << (2 * i))) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << (2 * i + 1))) + nv50_graph_construct_xfer_tp(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + } else { + /* Strand 0 */ + ctx->ctxvals_pos = offset; + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0x385, 0); + else + xf_emit(ctx, 0x384, 0); + nv50_graph_construct_gene_m2mf(ctx); + xf_emit(ctx, 0x950, 0); + nv50_graph_construct_gene_unk10(ctx); + xf_emit(ctx, 1, 0x0fac6881); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 1, 1); + xf_emit(ctx, 3, 0); + } + nv50_graph_construct_gene_unk8(ctx); + if (dev_priv->chipset == 0xa0) + xf_emit(ctx, 0x189, 0); + else if (dev_priv->chipset == 0xa3) + xf_emit(ctx, 0xd5, 0); + else if (dev_priv->chipset == 0xa5) + xf_emit(ctx, 0x99, 0); + else if (dev_priv->chipset == 0xaa) + xf_emit(ctx, 0x65, 0); + else + xf_emit(ctx, 0x6d, 0); + nv50_graph_construct_gene_unk9(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 1 */ + ctx->ctxvals_pos = offset + 1; + nv50_graph_construct_gene_unk1(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 2 */ + ctx->ctxvals_pos = offset + 2; + if (dev_priv->chipset == 0xa0) { + nv50_graph_construct_gene_unk2(ctx); + } + xf_emit(ctx, 0x36, 0); + nv50_graph_construct_gene_unk5(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 3 */ + ctx->ctxvals_pos = offset + 3; + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + nv50_graph_construct_gene_unk6(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 4 */ + ctx->ctxvals_pos = offset + 4; + if (dev_priv->chipset == 0xa0) + xf_emit(ctx, 0xa80, 0); + else if (dev_priv->chipset == 0xa3) + xf_emit(ctx, 0xa7c, 0); + else + xf_emit(ctx, 0xa7a, 0); + xf_emit(ctx, 1, 0x3fffff); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x1fff); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 5 */ + ctx->ctxvals_pos = offset + 5; + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 0xb, 0); + xf_emit(ctx, 2, 0x4e3bfdf); + xf_emit(ctx, 3, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 2, 0x4e3bfdf); + xf_emit(ctx, 2, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 0); + for (i = 0; i < 8; i++) + if (units & (1<<(i+16))) + nv50_graph_construct_gene_ropc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 6 */ + ctx->ctxvals_pos = offset + 6; + nv50_graph_construct_gene_unk3(ctx); + xf_emit(ctx, 0xb, 0); + nv50_graph_construct_gene_unk4(ctx); + nv50_graph_construct_gene_unk7(ctx); + if (units & (1 << 0)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 1)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 2)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 3)) + nv50_graph_construct_xfer_tp(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 7 */ + ctx->ctxvals_pos = offset + 7; + if (dev_priv->chipset == 0xa0) { + if (units & (1 << 4)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 5)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 6)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 7)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 8)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 9)) + nv50_graph_construct_xfer_tp(ctx); + } else { + nv50_graph_construct_gene_unk2(ctx); + } + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + + ctx->ctxvals_pos = offset + size * 8; + ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f; + cp_lsr (ctx, offset); + cp_out (ctx, CP_SET_XFER_POINTER); + cp_lsr (ctx, size); + cp_out (ctx, CP_SEEK_1); + cp_out (ctx, CP_XFER_1); + cp_wait(ctx, XFER, BUSY); +} + +/* + * non-trivial demagiced parts of ctx init go here + */ + +static void +nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx) +{ + /* m2mf state */ + xf_emit (ctx, 1, 0); /* DMA_NOTIFY instance >> 4 */ + xf_emit (ctx, 1, 0); /* DMA_BUFFER_IN instance >> 4 */ + xf_emit (ctx, 1, 0); /* DMA_BUFFER_OUT instance >> 4 */ + xf_emit (ctx, 1, 0); /* OFFSET_IN */ + xf_emit (ctx, 1, 0); /* OFFSET_OUT */ + xf_emit (ctx, 1, 0); /* PITCH_IN */ + xf_emit (ctx, 1, 0); /* PITCH_OUT */ + xf_emit (ctx, 1, 0); /* LINE_LENGTH */ + xf_emit (ctx, 1, 0); /* LINE_COUNT */ + xf_emit (ctx, 1, 0x21); /* FORMAT: bits 0-4 INPUT_INC, bits 5-9 OUTPUT_INC */ + xf_emit (ctx, 1, 1); /* LINEAR_IN */ + xf_emit (ctx, 1, 0x2); /* TILING_MODE_IN: bits 0-2 y tiling, bits 3-5 z tiling */ + xf_emit (ctx, 1, 0x100); /* TILING_PITCH_IN */ + xf_emit (ctx, 1, 0x100); /* TILING_HEIGHT_IN */ + xf_emit (ctx, 1, 1); /* TILING_DEPTH_IN */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_IN_Z */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_IN */ + xf_emit (ctx, 1, 1); /* LINEAR_OUT */ + xf_emit (ctx, 1, 0x2); /* TILING_MODE_OUT: bits 0-2 y tiling, bits 3-5 z tiling */ + xf_emit (ctx, 1, 0x100); /* TILING_PITCH_OUT */ + xf_emit (ctx, 1, 0x100); /* TILING_HEIGHT_OUT */ + xf_emit (ctx, 1, 1); /* TILING_DEPTH_OUT */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT_Z */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT */ + xf_emit (ctx, 1, 0); /* OFFSET_IN_HIGH */ + xf_emit (ctx, 1, 0); /* OFFSET_OUT_HIGH */ +} + +static void +nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* end of area 2 on pre-NVA0, area 1 on NVAx */ + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x80); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0x80c14); + xf_emit(ctx, 1, 0); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); + else + xf_emit(ctx, 1, 0x7ff); + switch (dev_priv->chipset) { + case 0x50: + case 0x86: + case 0x98: + case 0xaa: + case 0xac: + xf_emit(ctx, 0x542, 0); + break; + case 0x84: + case 0x92: + case 0x94: + case 0x96: + xf_emit(ctx, 0x942, 0); + break; + case 0xa0: + case 0xa3: + xf_emit(ctx, 0x2042, 0); + break; + case 0xa5: + case 0xa8: + xf_emit(ctx, 0x842, 0); + break; + } + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x80); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x27); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x26); + xf_emit(ctx, 3, 0); +} + +static void +nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx) +{ + /* end of area 2 on pre-NVA0, area 1 on NVAx */ + xf_emit(ctx, 0x10, 0x04000000); + xf_emit(ctx, 0x24, 0); + xf_emit(ctx, 2, 0x04e3bfdf); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x1fe21); +} + +static void +nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* middle of area 2 on pre-NVA0, beginning of area 2 on NVA0, area 7 on >NVA0 */ + if (dev_priv->chipset != 0x50) { + xf_emit(ctx, 5, 0); + xf_emit(ctx, 1, 0x80c14); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x804); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0x8100c12); + } + xf_emit(ctx, 1, 0); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x10); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 3, 0); + else + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0x804); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x1a); + if (dev_priv->chipset != 0x50) + xf_emit(ctx, 1, 0x7f); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x80c14); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x8100c12); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x10); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x8100c12); + xf_emit(ctx, 6, 0); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); + else + xf_emit(ctx, 1, 0x7ff); + xf_emit(ctx, 1, 0x80c14); + xf_emit(ctx, 0x38, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x10); + xf_emit(ctx, 0x38, 0); + xf_emit(ctx, 2, 0x88); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 0x16, 0); + xf_emit(ctx, 1, 0x26); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x3f800000); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 4, 0); + else + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x1a); + xf_emit(ctx, 1, 0x10); + if (dev_priv->chipset != 0x50) + xf_emit(ctx, 0x28, 0); + else + xf_emit(ctx, 0x25, 0); + xf_emit(ctx, 1, 0x52); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x26); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x1a); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x00ffff00); + xf_emit(ctx, 1, 0); +} + +static void +nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* end of area 0 on pre-NVA0, beginning of area 6 on NVAx */ + xf_emit(ctx, 1, 0x3f); + xf_emit(ctx, 0xa, 0); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 2, 0x04000000); + xf_emit(ctx, 8, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 4); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 0x10, 0); + else + xf_emit(ctx, 0x11, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x1001); + xf_emit(ctx, 4, 0xffff); + xf_emit(ctx, 0x20, 0); + xf_emit(ctx, 0x10, 0x3f800000); + xf_emit(ctx, 1, 0x10); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 1, 0); + else + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 3); + xf_emit(ctx, 2, 0); +} + +static void +nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx) +{ + /* middle of area 0 on pre-NVA0, middle of area 6 on NVAx */ + xf_emit(ctx, 2, 0x04000000); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x80); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x80); + xf_emit(ctx, 1, 0); +} + +static void +nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* middle of area 0 on pre-NVA0 [after m2mf], end of area 2 on NVAx */ + xf_emit(ctx, 2, 4); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0x1c4d, 0); + else + xf_emit(ctx, 0x1c4b, 0); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0x8100c12); + if (dev_priv->chipset != 0x50) + xf_emit(ctx, 1, 3); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x8100c12); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x80c14); + xf_emit(ctx, 1, 1); + if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0x80c14); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x8100c12); + xf_emit(ctx, 1, 0x27); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x3c1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x16, 0); + xf_emit(ctx, 1, 0x8100c12); + xf_emit(ctx, 1, 0); +} + +static void +nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* beginning of area 1 on pre-NVA0 [after m2mf], area 3 on NVAx */ + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0xf); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 8, 0); + else + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0x20); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0x11, 0); + else if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 0xf, 0); + else + xf_emit(ctx, 0xe, 0); + xf_emit(ctx, 1, 0x1a); + xf_emit(ctx, 0xd, 0); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 8); + xf_emit(ctx, 1, 0); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); + else + xf_emit(ctx, 1, 0x7ff); + if (dev_priv->chipset == 0xa8) + xf_emit(ctx, 1, 0x1e00); + xf_emit(ctx, 0xc, 0); + xf_emit(ctx, 1, 0xf); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 0x125, 0); + else if (dev_priv->chipset < 0xa0) + xf_emit(ctx, 0x126, 0); + else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) + xf_emit(ctx, 0x124, 0); + else + xf_emit(ctx, 0x1f7, 0); + xf_emit(ctx, 1, 0xf); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 3, 0); + else + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0xa1, 0); + else + xf_emit(ctx, 0x5a, 0); + xf_emit(ctx, 1, 0xf); + if (dev_priv->chipset < 0xa0) + xf_emit(ctx, 0x834, 0); + else if (dev_priv->chipset == 0xa0) + xf_emit(ctx, 0x1873, 0); + else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0x8ba, 0); + else + xf_emit(ctx, 0x833, 0); + xf_emit(ctx, 1, 0xf); + xf_emit(ctx, 0xf, 0); +} + +static void +nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 6 on NVAx */ + xf_emit(ctx, 2, 0); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 2, 1); + else + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0x100); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 8); + xf_emit(ctx, 5, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 3, 1); + xf_emit(ctx, 1, 0xcf); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 6, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 3, 1); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x15); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x4444480); + xf_emit(ctx, 0x37, 0); +} + +static void +nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx) +{ + /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 0 on NVAx */ + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0x8100c12); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0x100); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x10001); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x10001); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x10001); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 2); +} + +static void +nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* middle of area 2 on pre-NVA0 [after m2mf], end of area 0 on NVAx */ + xf_emit(ctx, 1, 0x3f800000); + xf_emit(ctx, 6, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0x1a); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x12, 0); + xf_emit(ctx, 1, 0x00ffff00); + xf_emit(ctx, 6, 0); + xf_emit(ctx, 1, 0xf); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 0xf, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 2, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 3); + else if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 2, 0x04000000); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 5); + xf_emit(ctx, 1, 0x52); + if (dev_priv->chipset == 0x50) { + xf_emit(ctx, 0x13, 0); + } else { + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 1); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0x11, 0); + else + xf_emit(ctx, 0x10, 0); + } + xf_emit(ctx, 0x10, 0x3f800000); + xf_emit(ctx, 1, 0x10); + xf_emit(ctx, 0x26, 0); + xf_emit(ctx, 1, 0x8100c12); + xf_emit(ctx, 1, 5); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 4, 0xffff); + if (dev_priv->chipset != 0x50) + xf_emit(ctx, 1, 3); + if (dev_priv->chipset < 0xa0) + xf_emit(ctx, 0x1f, 0); + else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0xc, 0); + else + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x00ffff00); + xf_emit(ctx, 1, 0x1a); + if (dev_priv->chipset != 0x50) { + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 3); + } + if (dev_priv->chipset < 0xa0) + xf_emit(ctx, 0x26, 0); + else + xf_emit(ctx, 0x3c, 0); + xf_emit(ctx, 1, 0x102); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 4, 4); + if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 8, 0); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); + else + xf_emit(ctx, 1, 0x7ff); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x102); + xf_emit(ctx, 9, 0); + xf_emit(ctx, 4, 4); + xf_emit(ctx, 0x2c, 0); +} + +static void +nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int magic2; + if (dev_priv->chipset == 0x50) { + magic2 = 0x00003e60; + } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) { + magic2 = 0x001ffe67; + } else { + magic2 = 0x00087e67; + } + xf_emit(ctx, 8, 0); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, magic2); + xf_emit(ctx, 4, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 7, 0); + if (dev_priv->chipset >= 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 0x15); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x10); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 4, 0); + if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x92 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa0) { + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0x400); + xf_emit(ctx, 1, 0x300); + xf_emit(ctx, 1, 0x1001); + if (dev_priv->chipset != 0xa0) { + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 0); + else + xf_emit(ctx, 1, 0x15); + } + xf_emit(ctx, 3, 0); + } + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 8, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x10); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x13, 0); + xf_emit(ctx, 1, 0x10); + xf_emit(ctx, 0x10, 0); + xf_emit(ctx, 0x10, 0x3f800000); + xf_emit(ctx, 0x19, 0); + xf_emit(ctx, 1, 0x10); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x3f); + xf_emit(ctx, 6, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + if (dev_priv->chipset >= 0xa0) { + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x1001); + xf_emit(ctx, 0xb, 0); + } else { + xf_emit(ctx, 0xc, 0); + } + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0xf); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x11); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 4, 0); + else + xf_emit(ctx, 6, 0); + xf_emit(ctx, 3, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, magic2); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x0fac6881); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 1, 0); + xf_emit(ctx, 0x18, 1); + xf_emit(ctx, 8, 2); + xf_emit(ctx, 8, 1); + xf_emit(ctx, 8, 2); + xf_emit(ctx, 8, 1); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 5, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x16, 0); + } else { + if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 0x1b, 0); + else + xf_emit(ctx, 0x15, 0); + } + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 2, 1); + if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 4, 0); + else + xf_emit(ctx, 3, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 0x10, 1); + xf_emit(ctx, 8, 2); + xf_emit(ctx, 0x10, 1); + xf_emit(ctx, 8, 2); + xf_emit(ctx, 8, 1); + xf_emit(ctx, 3, 0); + } + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x5b, 0); +} + +static void +nv50_graph_construct_xfer_tp_x1(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int magic3; + if (dev_priv->chipset == 0x50) + magic3 = 0x1000; + else if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8) + magic3 = 0x1e00; + else + magic3 = 0; + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 4); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0x24, 0); + else if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 0x14, 0); + else + xf_emit(ctx, 0x15, 0); + xf_emit(ctx, 2, 4); + if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 1, 0x03020100); + else + xf_emit(ctx, 1, 0x00608080); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 2, 4); + xf_emit(ctx, 1, 0x80); + if (magic3) + xf_emit(ctx, 1, magic3); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 0x24, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0x80); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0x03020100); + xf_emit(ctx, 1, 3); + if (magic3) + xf_emit(ctx, 1, magic3); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 3); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 4); + if (dev_priv->chipset == 0x94 || dev_priv->chipset == 0x96) + xf_emit(ctx, 0x1024, 0); + else if (dev_priv->chipset < 0xa0) + xf_emit(ctx, 0xa24, 0); + else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) + xf_emit(ctx, 0x214, 0); + else + xf_emit(ctx, 0x414, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 3); + xf_emit(ctx, 2, 0); +} + +static void +nv50_graph_construct_xfer_tp_x2(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int magic1, magic2; + if (dev_priv->chipset == 0x50) { + magic1 = 0x3ff; + magic2 = 0x00003e60; + } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) { + magic1 = 0x7ff; + magic2 = 0x001ffe67; + } else { + magic1 = 0x7ff; + magic2 = 0x00087e67; + } + xf_emit(ctx, 3, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0xc, 0); + xf_emit(ctx, 1, 0xf); + xf_emit(ctx, 0xb, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 4, 0xffff); + xf_emit(ctx, 8, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 5, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 1, 3); + xf_emit(ctx, 1, 0); + } else if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0xa, 0); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 1, 2); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 1, 0); + xf_emit(ctx, 0x18, 1); + xf_emit(ctx, 8, 2); + xf_emit(ctx, 8, 1); + xf_emit(ctx, 8, 2); + xf_emit(ctx, 8, 1); + xf_emit(ctx, 1, 0); + } + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 3, 0xcf); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0xa, 0); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 8, 1); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 1, 0xf); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, magic2); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x11); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 2, 1); + else + xf_emit(ctx, 1, 1); + if(dev_priv->chipset == 0x50) + xf_emit(ctx, 1, 0); + else + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 5, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, magic1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x28, 0); + xf_emit(ctx, 8, 8); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 8, 0x400); + xf_emit(ctx, 8, 0x300); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0xf); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x20); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 0x100); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x40); + xf_emit(ctx, 1, 0x100); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 3); + xf_emit(ctx, 4, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, magic2); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 9, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x400); + xf_emit(ctx, 1, 0x300); + xf_emit(ctx, 1, 0x1001); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 4, 0); + else + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 1, 0xf); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 0x15, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 3, 0); + } else + xf_emit(ctx, 0x17, 0); + if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 1, 0x0fac6881); + xf_emit(ctx, 1, magic2); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 3, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 2, 1); + else + xf_emit(ctx, 1, 1); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 2, 0); + else if (dev_priv->chipset != 0x50) + xf_emit(ctx, 1, 0); +} + +static void +nv50_graph_construct_xfer_tp_x3(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 2, 0); + else + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0x2a712488); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x4085c000); + xf_emit(ctx, 1, 0x40); + xf_emit(ctx, 1, 0x100); + xf_emit(ctx, 1, 0x10100); + xf_emit(ctx, 1, 0x02800000); +} + +static void +nv50_graph_construct_xfer_tp_x4(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + xf_emit(ctx, 2, 0x04e3bfdf); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x00ffff00); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 2, 1); + else + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x00ffff00); + xf_emit(ctx, 8, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0x30201000); + xf_emit(ctx, 1, 0x70605040); + xf_emit(ctx, 1, 0xb8a89888); + xf_emit(ctx, 1, 0xf8e8d8c8); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x1a); +} + +static void +nv50_graph_construct_xfer_tp_x5(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 0xfac6881); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 1); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 0xb, 0); + else + xf_emit(ctx, 0xa, 0); + xf_emit(ctx, 8, 1); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0xfac6881); + xf_emit(ctx, 1, 0xf); + xf_emit(ctx, 7, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 1); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 6, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 6, 0); + } else { + xf_emit(ctx, 0xb, 0); + } +} + +static void +nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + if (dev_priv->chipset < 0xa0) { + nv50_graph_construct_xfer_tp_x1(ctx); + nv50_graph_construct_xfer_tp_x2(ctx); + nv50_graph_construct_xfer_tp_x3(ctx); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 0xf, 0); + else + xf_emit(ctx, 0x12, 0); + nv50_graph_construct_xfer_tp_x4(ctx); + } else { + nv50_graph_construct_xfer_tp_x3(ctx); + if (dev_priv->chipset < 0xaa) + xf_emit(ctx, 0xc, 0); + else + xf_emit(ctx, 0xa, 0); + nv50_graph_construct_xfer_tp_x2(ctx); + nv50_graph_construct_xfer_tp_x5(ctx); + nv50_graph_construct_xfer_tp_x4(ctx); + nv50_graph_construct_xfer_tp_x1(ctx); + } +} + +static void +nv50_graph_construct_xfer_tp2(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int i, mpcnt; + if (dev_priv->chipset == 0x98 || dev_priv->chipset == 0xaa) + mpcnt = 1; + else if (dev_priv->chipset < 0xa0 || dev_priv->chipset >= 0xa8) + mpcnt = 2; + else + mpcnt = 3; + for (i = 0; i < mpcnt; i++) { + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x80); + xf_emit(ctx, 1, 0x80007004); + xf_emit(ctx, 1, 0x04000400); + if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 1, 0xc0); + xf_emit(ctx, 1, 0x1000); + xf_emit(ctx, 2, 0); + if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8) { + xf_emit(ctx, 1, 0xe00); + xf_emit(ctx, 1, 0x1e00); + } + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 2, 0x1000); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 2); + if (dev_priv->chipset >= 0xaa) + xf_emit(ctx, 0xb, 0); + else if (dev_priv->chipset >= 0xa0) + xf_emit(ctx, 0xc, 0); + else + xf_emit(ctx, 0xa, 0); + } + xf_emit(ctx, 1, 0x08100c12); + xf_emit(ctx, 1, 0); + if (dev_priv->chipset >= 0xa0) { + xf_emit(ctx, 1, 0x1fe21); + } + xf_emit(ctx, 5, 0); + xf_emit(ctx, 4, 0xffff); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 2, 0x10001); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 0x1fe21); + xf_emit(ctx, 1, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 1); + xf_emit(ctx, 4, 0); + xf_emit(ctx, 1, 0x08100c12); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 8, 0); + xf_emit(ctx, 1, 0xfac6881); + xf_emit(ctx, 1, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) + xf_emit(ctx, 1, 3); + xf_emit(ctx, 3, 0); + xf_emit(ctx, 1, 4); + xf_emit(ctx, 9, 0); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 2, 1); + xf_emit(ctx, 1, 2); + xf_emit(ctx, 3, 1); + xf_emit(ctx, 1, 0); + if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { + xf_emit(ctx, 8, 2); + xf_emit(ctx, 0x10, 1); + xf_emit(ctx, 8, 2); + xf_emit(ctx, 0x18, 1); + xf_emit(ctx, 3, 0); + } + xf_emit(ctx, 1, 4); + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 0x3a0, 0); + else if (dev_priv->chipset < 0x94) + xf_emit(ctx, 0x3a2, 0); + else if (dev_priv->chipset == 0x98 || dev_priv->chipset == 0xaa) + xf_emit(ctx, 0x39f, 0); + else + xf_emit(ctx, 0x3a3, 0); + xf_emit(ctx, 1, 0x11); + xf_emit(ctx, 1, 0); + xf_emit(ctx, 1, 1); + xf_emit(ctx, 0x2d, 0); +} + +static void +nv50_graph_construct_xfer2(struct nouveau_grctx *ctx) +{ + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int i; + uint32_t offset; + uint32_t units = nv_rd32 (ctx->dev, 0x1540); + int size = 0; + + offset = (ctx->ctxvals_pos+0x3f)&~0x3f; + + if (dev_priv->chipset < 0xa0) { + for (i = 0; i < 8; i++) { + ctx->ctxvals_pos = offset + i; + if (i == 0) + xf_emit(ctx, 1, 0x08100c12); + if (units & (1 << i)) + nv50_graph_construct_xfer_tp2(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + } else { + /* Strand 0: TPs 0, 1 */ + ctx->ctxvals_pos = offset; + xf_emit(ctx, 1, 0x08100c12); + if (units & (1 << 0)) + nv50_graph_construct_xfer_tp2(ctx); + if (units & (1 << 1)) + nv50_graph_construct_xfer_tp2(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 0: TPs 2, 3 */ + ctx->ctxvals_pos = offset + 1; + if (units & (1 << 2)) + nv50_graph_construct_xfer_tp2(ctx); + if (units & (1 << 3)) + nv50_graph_construct_xfer_tp2(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 0: TPs 4, 5, 6 */ + ctx->ctxvals_pos = offset + 2; + if (units & (1 << 4)) + nv50_graph_construct_xfer_tp2(ctx); + if (units & (1 << 5)) + nv50_graph_construct_xfer_tp2(ctx); + if (units & (1 << 6)) + nv50_graph_construct_xfer_tp2(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 0: TPs 7, 8, 9 */ + ctx->ctxvals_pos = offset + 3; + if (units & (1 << 7)) + nv50_graph_construct_xfer_tp2(ctx); + if (units & (1 << 8)) + nv50_graph_construct_xfer_tp2(ctx); + if (units & (1 << 9)) + nv50_graph_construct_xfer_tp2(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + ctx->ctxvals_pos = offset + size * 8; + ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f; + cp_lsr (ctx, offset); + cp_out (ctx, CP_SET_XFER_POINTER); + cp_lsr (ctx, size); + cp_out (ctx, CP_SEEK_2); + cp_out (ctx, CP_XFER_2); + cp_wait(ctx, XFER, BUSY); +} diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c index f0dc4e36ef05..5f21df31f3aa 100644 --- a/drivers/gpu/drm/nouveau/nv50_instmem.c +++ b/drivers/gpu/drm/nouveau/nv50_instmem.c @@ -63,9 +63,10 @@ nv50_instmem_init(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan; uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size; + uint32_t save_nv001700; + uint64_t v; struct nv50_instmem_priv *priv; int ret, i; - uint32_t v, save_nv001700; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) @@ -76,17 +77,12 @@ nv50_instmem_init(struct drm_device *dev) for (i = 0x1700; i <= 0x1710; i += 4) priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i); - if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) - dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10) << 12; - else - dev_priv->vram_sys_base = 0; - /* Reserve the last MiB of VRAM, we should probably try to avoid * setting up the below tables over the top of the VBIOS image at * some point. */ dev_priv->ramin_rsvd_vram = 1 << 20; - c_offset = nouveau_mem_fb_amount(dev) - dev_priv->ramin_rsvd_vram; + c_offset = dev_priv->vram_size - dev_priv->ramin_rsvd_vram; c_size = 128 << 10; c_vmpd = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200; c_ramfc = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20; @@ -106,7 +102,7 @@ nv50_instmem_init(struct drm_device *dev) dev_priv->vm_gart_size = NV50_VM_BLOCK; dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size; - dev_priv->vm_vram_size = nouveau_mem_fb_amount(dev); + dev_priv->vm_vram_size = dev_priv->vram_size; if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM) dev_priv->vm_vram_size = NV50_VM_MAX_VRAM; dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK); @@ -189,8 +185,8 @@ nv50_instmem_init(struct drm_device *dev) i = 0; while (v < dev_priv->vram_sys_base + c_offset + c_size) { - BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v); - BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000); + BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, lower_32_bits(v)); + BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, upper_32_bits(v)); v += 0x1000; i += 8; } @@ -390,7 +386,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, if (gpuobj->im_backing) return -EINVAL; - *sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1); + *sz = ALIGN(*sz, NV50_INSTMEM_PAGE_SIZE); if (*sz == 0) return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index c2fff543b06f..0c68698f23df 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -211,7 +211,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, mode_ctl = 0x0200; break; case OUTPUT_DP: - mode_ctl |= 0x00050000; + mode_ctl |= (nv_encoder->dp.mc_unknown << 16); if (nv_encoder->dcb->sorconf.link & 1) mode_ctl |= 0x00000800; else @@ -274,6 +274,7 @@ static const struct drm_encoder_funcs nv50_sor_encoder_funcs = { int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry) { + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_encoder *nv_encoder = NULL; struct drm_encoder *encoder; bool dum; @@ -319,5 +320,27 @@ nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry) encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; + if (nv_encoder->dcb->type == OUTPUT_DP) { + uint32_t mc, or = nv_encoder->or; + + if (dev_priv->chipset < 0x90 || + dev_priv->chipset == 0x92 || dev_priv->chipset == 0xa0) + mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(or)); + else + mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(or)); + + switch ((mc & 0x00000f00) >> 8) { + case 8: + case 9: + nv_encoder->dp.mc_unknown = (mc & 0x000f0000) >> 16; + break; + default: + break; + } + + if (!nv_encoder->dp.mc_unknown) + nv_encoder->dp.mc_unknown = 5; + } + return 0; } |