diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau')
46 files changed, 5165 insertions, 1846 deletions
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index de70959b9ed5..ca1639918f57 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -11,6 +11,8 @@ config DRM_NOUVEAU select FRAMEBUFFER_CONSOLE if !EXPERT select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT + select ACPI_WMI if ACPI + select MXM_WMI if ACPI help Choose this option for open-source nVidia support. diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index e12c97fd8db8..0583677e4581 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -20,6 +20,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv40_graph.o nv50_graph.o nvc0_graph.o \ nv40_grctx.o nv50_grctx.o nvc0_grctx.o \ nv84_crypt.o \ + nva3_copy.o nvc0_copy.o \ + nv40_mpeg.o nv50_mpeg.o \ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ nv50_cursor.o nv50_display.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index a54238058dc5..f0d459bb46e4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -4,6 +4,8 @@ #include <acpi/acpi_drivers.h> #include <acpi/acpi_bus.h> #include <acpi/video.h> +#include <acpi/acpi.h> +#include <linux/mxm-wmi.h> #include "drmP.h" #include "drm.h" @@ -35,15 +37,71 @@ static struct nouveau_dsm_priv { bool dsm_detected; + bool optimus_detected; acpi_handle dhandle; acpi_handle rom_handle; } nouveau_dsm_priv; +#define NOUVEAU_DSM_HAS_MUX 0x1 +#define NOUVEAU_DSM_HAS_OPT 0x2 + static const char nouveau_dsm_muid[] = { 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, }; +static const char nouveau_op_dsm_muid[] = { + 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47, + 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, +}; + +static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *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; + + input.count = 4; + input.pointer = params; + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(nouveau_op_dsm_muid); + params[0].buffer.pointer = (char *)nouveau_op_dsm_muid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = 0x00000100; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = func; + params[3].type = ACPI_TYPE_BUFFER; + params[3].buffer.length = 0; + + err = acpi_evaluate_object(handle, "_DSM", &input, &output); + if (err) { + printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); + return err; + } + + obj = (union acpi_object *)output.pointer; + + if (obj->type == ACPI_TYPE_INTEGER) + if (obj->integer.value == 0x80000002) { + return -ENODEV; + } + + if (obj->type == ACPI_TYPE_BUFFER) { + if (obj->buffer.length == 4 && result) { + *result = 0; + *result |= obj->buffer.pointer[0]; + *result |= (obj->buffer.pointer[1] << 8); + *result |= (obj->buffer.pointer[2] << 16); + *result |= (obj->buffer.pointer[3] << 24); + } + } + + kfree(output.pointer); + return 0; +} + static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -92,6 +150,8 @@ static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) { + mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); + mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); } @@ -148,11 +208,11 @@ static struct vga_switcheroo_handler nouveau_dsm_handler = { .get_client_id = nouveau_dsm_get_client_id, }; -static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) +static int nouveau_dsm_pci_probe(struct pci_dev *pdev) { acpi_handle dhandle, nvidia_handle; acpi_status status; - int ret; + int ret, retval = 0; uint32_t result; dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); @@ -166,11 +226,17 @@ static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); - if (ret < 0) - return false; + if (ret == 0) + retval |= NOUVEAU_DSM_HAS_MUX; - nouveau_dsm_priv.dhandle = dhandle; - return true; + ret = nouveau_optimus_dsm(dhandle, 0, 0, &result); + if (ret == 0) + retval |= NOUVEAU_DSM_HAS_OPT; + + if (retval) + nouveau_dsm_priv.dhandle = dhandle; + + return retval; } static bool nouveau_dsm_detect(void) @@ -179,22 +245,42 @@ static bool nouveau_dsm_detect(void) struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; struct pci_dev *pdev = NULL; int has_dsm = 0; + int has_optimus; int vga_count = 0; + bool guid_valid; + int retval; + bool ret = false; + + /* lookup the MXM GUID */ + guid_valid = mxm_wmi_supported(); + if (guid_valid) + printk("MXM: GUID detected in BIOS\n"); + + /* now do DSM detection */ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { vga_count++; - has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); + retval = nouveau_dsm_pci_probe(pdev); + printk("ret val is %d\n", retval); + if (retval & NOUVEAU_DSM_HAS_MUX) + has_dsm |= 1; + if (retval & NOUVEAU_DSM_HAS_OPT) + has_optimus = 1; } - if (vga_count == 2 && has_dsm) { + if (vga_count == 2 && has_dsm && guid_valid) { acpi_get_name(nouveau_dsm_priv.dhandle, 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; + ret = true; } - return false; + + if (has_optimus == 1) + nouveau_dsm_priv.optimus_detected = true; + + return ret; } void nouveau_register_dsm_handler(void) @@ -247,7 +333,7 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev) acpi_status status; acpi_handle dhandle, rom_handle; - if (!nouveau_dsm_priv.dsm_detected) + if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected) return false; dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 90aef64b76f2..729d5fd7c88d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -5049,11 +5049,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims pll_lim->vco1.max_n = record[11]; pll_lim->min_p = record[12]; pll_lim->max_p = record[13]; - /* where did this go to?? */ - if ((entry[0] & 0xf0) == 0x80) - pll_lim->refclk = 27000; - else - pll_lim->refclk = 100000; + pll_lim->refclk = ROM16(entry[9]) * 1000; } /* @@ -6035,6 +6031,7 @@ parse_dcb_connector_table(struct nvbios *bios) case DCB_CONNECTOR_DVI_I: case DCB_CONNECTOR_DVI_D: case DCB_CONNECTOR_LVDS: + case DCB_CONNECTOR_LVDS_SPWG: case DCB_CONNECTOR_DP: case DCB_CONNECTOR_eDP: case DCB_CONNECTOR_HDMI_0: diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 8a54fa7edf5c..050c314119df 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -82,6 +82,7 @@ enum dcb_connector_type { DCB_CONNECTOR_DVI_I = 0x30, DCB_CONNECTOR_DVI_D = 0x31, DCB_CONNECTOR_LVDS = 0x40, + DCB_CONNECTOR_LVDS_SPWG = 0x41, DCB_CONNECTOR_DP = 0x46, DCB_CONNECTOR_eDP = 0x47, DCB_CONNECTOR_HDMI_0 = 0x60, diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index 4cea35c57d15..a7583a8ddb01 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -268,9 +268,8 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan) struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt; unsigned long flags; + int i; /* decrement the refcount, and we're done if there's still refs */ if (likely(!atomic_dec_and_test(&chan->users))) { @@ -294,19 +293,12 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan) /* boot it off the hardware */ 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 without the context - * switch lock held, otherwise the irq handler is unable to - * process them. - */ - if (pgraph->channel(dev) == chan) - nouveau_wait_for_idle(dev); - /* destroy the engine specific contexts */ pfifo->destroy_context(chan); - pgraph->destroy_context(chan); - if (pcrypt->destroy_context) - pcrypt->destroy_context(chan); + for (i = 0; i < NVOBJ_ENGINE_NR; i++) { + if (chan->engctx[i]) + dev_priv->eng[i]->context_del(chan, i); + } pfifo->reassign(dev, true); @@ -414,7 +406,7 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, struct nouveau_channel *chan; int ret; - if (dev_priv->engine.graph.accel_blocked) + if (!dev_priv->eng[NVOBJ_ENGINE_GR]) return -ENODEV; if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 7ae151109a66..1595d0b6e815 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -442,7 +442,7 @@ nouveau_connector_set_property(struct drm_connector *connector, } /* LVDS always needs gpu scaling */ - if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS && + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && value == DRM_MODE_SCALE_NONE) return -EINVAL; @@ -650,6 +650,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) ret = get_slave_funcs(encoder)->get_modes(encoder, connector); if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS || + nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG || nv_connector->dcb->type == DCB_CONNECTOR_eDP) ret += nouveau_connector_scaler_modes_add(connector); @@ -810,6 +811,7 @@ nouveau_connector_create(struct drm_device *dev, int index) type = DRM_MODE_CONNECTOR_HDMIA; break; case DCB_CONNECTOR_LVDS: + case DCB_CONNECTOR_LVDS_SPWG: type = DRM_MODE_CONNECTOR_LVDS; funcs = &nouveau_connector_funcs_lvds; break; @@ -838,7 +840,7 @@ nouveau_connector_create(struct drm_device *dev, int index) drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); /* Check if we need dithering enabled */ - if (dcb->type == DCB_CONNECTOR_LVDS) { + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { bool dummy, is_24bit = false; ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit); @@ -883,7 +885,7 @@ nouveau_connector_create(struct drm_device *dev, int index) nv_connector->use_dithering ? DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); - if (dcb->type != DCB_CONNECTOR_LVDS) { + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) { if (dev_priv->card_type >= NV_50) connector->polled = DRM_CONNECTOR_POLL_HPD; else diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 764c15d537ba..eb514ea29377 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -276,7 +276,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct nouveau_fence *fence; int ret; - if (dev_priv->engine.graph.accel_blocked) + if (!dev_priv->channel) return -ENODEV; s = kzalloc(sizeof(*s), GFP_KERNEL); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 155ebdcbf06f..02c6f37d8bd7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -162,11 +162,10 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) struct drm_device *dev = pci_get_drvdata(pdev); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_channel *chan; struct drm_crtc *crtc; - int ret, i; + int ret, i, e; if (pm_state.event == PM_EVENT_PRETHAW) return 0; @@ -206,12 +205,17 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) nouveau_channel_idle(chan); } - pgraph->fifo_access(dev, false); - nouveau_wait_for_idle(dev); pfifo->reassign(dev, false); pfifo->disable(dev); pfifo->unload_context(dev); - pgraph->unload_context(dev); + + for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) { + if (dev_priv->eng[e]) { + ret = dev_priv->eng[e]->fini(dev, e); + if (ret) + goto out_abort; + } + } ret = pinstmem->suspend(dev); if (ret) { @@ -242,9 +246,12 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) out_abort: NV_INFO(dev, "Re-enabling acceleration..\n"); + for (e = e + 1; e < NVOBJ_ENGINE_NR; e++) { + if (dev_priv->eng[e]) + dev_priv->eng[e]->init(dev, e); + } pfifo->enable(dev); pfifo->reassign(dev, true); - pgraph->fifo_access(dev, true); return ret; } @@ -299,8 +306,10 @@ nouveau_pci_resume(struct pci_dev *pdev) engine->mc.init(dev); engine->timer.init(dev); engine->fb.init(dev); - engine->graph.init(dev); - engine->crypt.init(dev); + for (i = 0; i < NVOBJ_ENGINE_NR; i++) { + if (dev_priv->eng[i]) + dev_priv->eng[i]->init(dev, i); + } engine->fifo.init(dev); nouveau_irq_postinstall(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index a76514a209b3..9c56331941e2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -150,13 +150,12 @@ enum nouveau_flags { #define NVOBJ_ENGINE_SW 0 #define NVOBJ_ENGINE_GR 1 -#define NVOBJ_ENGINE_PPP 2 -#define NVOBJ_ENGINE_COPY 3 -#define NVOBJ_ENGINE_VP 4 -#define NVOBJ_ENGINE_CRYPT 5 -#define NVOBJ_ENGINE_BSP 6 -#define NVOBJ_ENGINE_DISPLAY 0xcafe0001 -#define NVOBJ_ENGINE_INT 0xdeadbeef +#define NVOBJ_ENGINE_CRYPT 2 +#define NVOBJ_ENGINE_COPY0 3 +#define NVOBJ_ENGINE_COPY1 4 +#define NVOBJ_ENGINE_MPEG 5 +#define NVOBJ_ENGINE_DISPLAY 15 +#define NVOBJ_ENGINE_NR 16 #define NVOBJ_FLAG_DONT_MAP (1 << 0) #define NVOBJ_FLAG_ZERO_ALLOC (1 << 1) @@ -245,11 +244,8 @@ struct nouveau_channel { struct nouveau_gpuobj *cache; void *fifo_priv; - /* PGRAPH context */ - /* XXX may be merge 2 pointers as private data ??? */ - struct nouveau_gpuobj *ramin_grctx; - struct nouveau_gpuobj *crypt_ctx; - void *pgraph_ctx; + /* Execution engine contexts */ + void *engctx[NVOBJ_ENGINE_NR]; /* NV50 VM */ struct nouveau_vm *vm; @@ -298,6 +294,18 @@ struct nouveau_channel { } debugfs; }; +struct nouveau_exec_engine { + void (*destroy)(struct drm_device *, int engine); + int (*init)(struct drm_device *, int engine); + int (*fini)(struct drm_device *, int engine); + int (*context_new)(struct nouveau_channel *, int engine); + void (*context_del)(struct nouveau_channel *, int engine); + int (*object_new)(struct nouveau_channel *, int engine, + u32 handle, u16 class); + void (*set_tile_region)(struct drm_device *dev, int i); + void (*tlb_flush)(struct drm_device *, int engine); +}; + struct nouveau_instmem_engine { void *priv; @@ -364,30 +372,6 @@ struct nouveau_fifo_engine { void (*tlb_flush)(struct drm_device *dev); }; -struct nouveau_pgraph_engine { - bool accel_blocked; - bool registered; - int grctx_size; - void *priv; - - /* NV2x/NV3x context table (0x400780) */ - struct nouveau_gpuobj *ctx_table; - - int (*init)(struct drm_device *); - void (*takedown)(struct drm_device *); - - void (*fifo_access)(struct drm_device *, bool); - - struct nouveau_channel *(*channel)(struct drm_device *); - int (*create_context)(struct nouveau_channel *); - void (*destroy_context)(struct nouveau_channel *); - int (*load_context)(struct nouveau_channel *); - int (*unload_context)(struct drm_device *); - void (*tlb_flush)(struct drm_device *dev); - - void (*set_tile_region)(struct drm_device *dev, int i); -}; - struct nouveau_display_engine { void *priv; int (*early_init)(struct drm_device *); @@ -426,6 +410,19 @@ struct nouveau_pm_voltage { int nr_level; }; +struct nouveau_pm_memtiming { + int id; + u32 reg_100220; + u32 reg_100224; + u32 reg_100228; + u32 reg_10022c; + u32 reg_100230; + u32 reg_100234; + u32 reg_100238; + u32 reg_10023c; + u32 reg_100240; +}; + #define NOUVEAU_PM_MAX_LEVEL 8 struct nouveau_pm_level { struct device_attribute dev_attr; @@ -436,11 +433,13 @@ struct nouveau_pm_level { u32 memory; u32 shader; u32 unk05; + u32 unk0a; u8 voltage; u8 fanspeed; u16 memscript; + struct nouveau_pm_memtiming *timing; }; struct nouveau_pm_temp_sensor_constants { @@ -457,17 +456,6 @@ struct nouveau_pm_threshold_temp { s16 fan_boost; }; -struct nouveau_pm_memtiming { - u32 reg_100220; - u32 reg_100224; - u32 reg_100228; - u32 reg_10022c; - u32 reg_100230; - u32 reg_100234; - u32 reg_100238; - u32 reg_10023c; -}; - struct nouveau_pm_memtimings { bool supported; struct nouveau_pm_memtiming *timing; @@ -499,16 +487,6 @@ struct nouveau_pm_engine { int (*temp_get)(struct drm_device *); }; -struct nouveau_crypt_engine { - bool registered; - - int (*init)(struct drm_device *); - void (*takedown)(struct drm_device *); - int (*create_context)(struct nouveau_channel *); - void (*destroy_context)(struct nouveau_channel *); - void (*tlb_flush)(struct drm_device *dev); -}; - struct nouveau_vram_engine { int (*init)(struct drm_device *); int (*get)(struct drm_device *, u64, u32 align, u32 size_nc, @@ -523,12 +501,10 @@ struct nouveau_engine { struct nouveau_mc_engine mc; struct nouveau_timer_engine timer; struct nouveau_fb_engine fb; - struct nouveau_pgraph_engine graph; struct nouveau_fifo_engine fifo; struct nouveau_display_engine display; struct nouveau_gpio_engine gpio; struct nouveau_pm_engine pm; - struct nouveau_crypt_engine crypt; struct nouveau_vram_engine vram; }; @@ -637,6 +613,7 @@ struct drm_nouveau_private { enum nouveau_card_type card_type; /* exact chipset, derived from NV_PMC_BOOT_0 */ int chipset; + int stepping; int flags; void __iomem *mmio; @@ -647,6 +624,7 @@ struct drm_nouveau_private { u32 ramin_base; bool ramin_available; struct drm_mm ramin_heap; + struct nouveau_exec_engine *eng[NVOBJ_ENGINE_NR]; struct list_head gpuobj_list; struct list_head classes; @@ -745,10 +723,6 @@ struct drm_nouveau_private { uint32_t crtc_owner; uint32_t dac_users[4]; - struct nouveau_suspend_resume { - uint32_t *ramin_copy; - } susres; - struct backlight_device *backlight; struct { @@ -757,8 +731,6 @@ struct drm_nouveau_private { struct nouveau_fbdev *nfbdev; struct apertures_struct *apertures; - - bool powered_down; }; static inline struct drm_nouveau_private * @@ -883,17 +855,27 @@ extern void nouveau_channel_ref(struct nouveau_channel *chan, extern void nouveau_channel_idle(struct nouveau_channel *chan); /* nouveau_object.c */ -#define NVOBJ_CLASS(d,c,e) do { \ +#define NVOBJ_ENGINE_ADD(d, e, p) do { \ + struct drm_nouveau_private *dev_priv = (d)->dev_private; \ + dev_priv->eng[NVOBJ_ENGINE_##e] = (p); \ +} while (0) + +#define NVOBJ_ENGINE_DEL(d, e) do { \ + struct drm_nouveau_private *dev_priv = (d)->dev_private; \ + dev_priv->eng[NVOBJ_ENGINE_##e] = NULL; \ +} while (0) + +#define NVOBJ_CLASS(d, c, e) do { \ int ret = nouveau_gpuobj_class_new((d), (c), NVOBJ_ENGINE_##e); \ if (ret) \ return ret; \ -} while(0) +} while (0) -#define NVOBJ_MTHD(d,c,m,e) do { \ +#define NVOBJ_MTHD(d, c, m, e) do { \ int ret = nouveau_gpuobj_mthd_new((d), (c), (m), (e)); \ if (ret) \ return ret; \ -} while(0) +} while (0) extern int nouveau_gpuobj_early_init(struct drm_device *); extern int nouveau_gpuobj_init(struct drm_device *); @@ -903,7 +885,7 @@ extern void nouveau_gpuobj_resume(struct drm_device *dev); extern int nouveau_gpuobj_class_new(struct drm_device *, u32 class, u32 eng); extern int nouveau_gpuobj_mthd_new(struct drm_device *, u32 class, u32 mthd, int (*exec)(struct nouveau_channel *, - u32 class, u32 mthd, u32 data)); + u32 class, u32 mthd, u32 data)); extern int nouveau_gpuobj_mthd_call(struct nouveau_channel *, u32, u32, u32); extern int nouveau_gpuobj_mthd_call2(struct drm_device *, int, u32, u32, u32); extern int nouveau_gpuobj_channel_init(struct nouveau_channel *, @@ -1137,81 +1119,50 @@ extern int nvc0_fifo_load_context(struct nouveau_channel *); extern int nvc0_fifo_unload_context(struct drm_device *); /* nv04_graph.c */ -extern int nv04_graph_init(struct drm_device *); -extern void nv04_graph_takedown(struct drm_device *); +extern int nv04_graph_create(struct drm_device *); extern void nv04_graph_fifo_access(struct drm_device *, bool); -extern struct nouveau_channel *nv04_graph_channel(struct drm_device *); -extern int nv04_graph_create_context(struct nouveau_channel *); -extern void nv04_graph_destroy_context(struct nouveau_channel *); -extern int nv04_graph_load_context(struct nouveau_channel *); -extern int nv04_graph_unload_context(struct drm_device *); +extern int nv04_graph_object_new(struct nouveau_channel *, int, u32, u16); extern int nv04_graph_mthd_page_flip(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data); extern struct nouveau_bitfield nv04_graph_nsource[]; /* nv10_graph.c */ -extern int nv10_graph_init(struct drm_device *); -extern void nv10_graph_takedown(struct drm_device *); +extern int nv10_graph_create(struct drm_device *); extern struct nouveau_channel *nv10_graph_channel(struct drm_device *); -extern int nv10_graph_create_context(struct nouveau_channel *); -extern void nv10_graph_destroy_context(struct nouveau_channel *); -extern int nv10_graph_load_context(struct nouveau_channel *); -extern int nv10_graph_unload_context(struct drm_device *); -extern void nv10_graph_set_tile_region(struct drm_device *dev, int i); extern struct nouveau_bitfield nv10_graph_intr[]; extern struct nouveau_bitfield nv10_graph_nstatus[]; /* nv20_graph.c */ -extern int nv20_graph_create_context(struct nouveau_channel *); -extern void nv20_graph_destroy_context(struct nouveau_channel *); -extern int nv20_graph_load_context(struct nouveau_channel *); -extern int nv20_graph_unload_context(struct drm_device *); -extern int nv20_graph_init(struct drm_device *); -extern void nv20_graph_takedown(struct drm_device *); -extern int nv30_graph_init(struct drm_device *); -extern void nv20_graph_set_tile_region(struct drm_device *dev, int i); +extern int nv20_graph_create(struct drm_device *); /* nv40_graph.c */ -extern int nv40_graph_init(struct drm_device *); -extern void nv40_graph_takedown(struct drm_device *); -extern struct nouveau_channel *nv40_graph_channel(struct drm_device *); -extern int nv40_graph_create_context(struct nouveau_channel *); -extern void nv40_graph_destroy_context(struct nouveau_channel *); -extern int nv40_graph_load_context(struct nouveau_channel *); -extern int nv40_graph_unload_context(struct drm_device *); +extern int nv40_graph_create(struct drm_device *); extern void nv40_grctx_init(struct nouveau_grctx *); -extern void nv40_graph_set_tile_region(struct drm_device *dev, int i); /* nv50_graph.c */ -extern int nv50_graph_init(struct drm_device *); -extern void nv50_graph_takedown(struct drm_device *); -extern void nv50_graph_fifo_access(struct drm_device *, bool); -extern struct nouveau_channel *nv50_graph_channel(struct drm_device *); -extern int nv50_graph_create_context(struct nouveau_channel *); -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 int nv50_graph_create(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); -extern void nv50_graph_tlb_flush(struct drm_device *dev); -extern void nv84_graph_tlb_flush(struct drm_device *dev); extern struct nouveau_enum nv50_data_error_names[]; +extern int nv50_graph_isr_chid(struct drm_device *dev, u64 inst); /* nvc0_graph.c */ -extern int nvc0_graph_init(struct drm_device *); -extern void nvc0_graph_takedown(struct drm_device *); -extern void nvc0_graph_fifo_access(struct drm_device *, bool); -extern struct nouveau_channel *nvc0_graph_channel(struct drm_device *); -extern int nvc0_graph_create_context(struct nouveau_channel *); -extern void nvc0_graph_destroy_context(struct nouveau_channel *); -extern int nvc0_graph_load_context(struct nouveau_channel *); -extern int nvc0_graph_unload_context(struct drm_device *); +extern int nvc0_graph_create(struct drm_device *); +extern int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst); /* nv84_crypt.c */ -extern int nv84_crypt_init(struct drm_device *dev); -extern void nv84_crypt_fini(struct drm_device *dev); -extern int nv84_crypt_create_context(struct nouveau_channel *); -extern void nv84_crypt_destroy_context(struct nouveau_channel *); -extern void nv84_crypt_tlb_flush(struct drm_device *dev); +extern int nv84_crypt_create(struct drm_device *); + +/* nva3_copy.c */ +extern int nva3_copy_create(struct drm_device *dev); + +/* nvc0_copy.c */ +extern int nvc0_copy_create(struct drm_device *dev, int engine); + +/* nv40_mpeg.c */ +extern int nv40_mpeg_create(struct drm_device *dev); + +/* nv50_mpeg.c */ +extern int nv50_mpeg_create(struct drm_device *dev); /* nv04_instmem.c */ extern int nv04_instmem_init(struct drm_device *); @@ -1402,8 +1353,8 @@ bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on); /* nv50_calc. */ int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, int *N1, int *M1, int *N2, int *M2, int *P); -int nv50_calc_pll2(struct drm_device *, struct pll_lims *, - int clk, int *N, int *fN, int *M, int *P); +int nva3_calc_pll(struct drm_device *, struct pll_lims *, + int clk, int *N, int *fN, int *M, int *P); #ifndef ioread32_native #ifdef __BIG_ENDIAN @@ -1579,6 +1530,13 @@ nv_match_device(struct drm_device *dev, unsigned device, dev->pdev->subsystem_device == sub_device; } +static inline void * +nv_engine(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + return (void *)dev_priv->eng[engine]; +} + /* returns 1 if device is one of the nv4x using the 0x4497 object class, * helpful to determine a number of other hardware features */ diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.h b/drivers/gpu/drm/nouveau/nouveau_grctx.h index 4a8ad1307fa4..86c2e374e938 100644 --- a/drivers/gpu/drm/nouveau/nouveau_grctx.h +++ b/drivers/gpu/drm/nouveau/nouveau_grctx.h @@ -87,10 +87,10 @@ _cp_bra(struct nouveau_grctx *ctx, u32 mod, int flag, int state, int name) cp_out(ctx, CP_BRA | (mod << 18) | ip | flag | (state ? 0 : CP_BRA_IF_CLEAR)); } -#define cp_bra(c,f,s,n) _cp_bra((c), 0, CP_FLAG_##f, CP_FLAG_##f##_##s, n) +#define cp_bra(c, f, s, n) _cp_bra((c), 0, CP_FLAG_##f, CP_FLAG_##f##_##s, n) #ifdef CP_BRA_MOD -#define cp_cal(c,f,s,n) _cp_bra((c), 1, CP_FLAG_##f, CP_FLAG_##f##_##s, n) -#define cp_ret(c,f,s) _cp_bra((c), 2, CP_FLAG_##f, CP_FLAG_##f##_##s, 0) +#define cp_cal(c, f, s, n) _cp_bra((c), 1, CP_FLAG_##f, CP_FLAG_##f##_##s, n) +#define cp_ret(c, f, s) _cp_bra((c), 2, CP_FLAG_##f, CP_FLAG_##f##_##s, 0) #endif static inline void @@ -98,14 +98,14 @@ _cp_wait(struct nouveau_grctx *ctx, int flag, int state) { cp_out(ctx, CP_WAIT | flag | (state ? CP_WAIT_SET : 0)); } -#define cp_wait(c,f,s) _cp_wait((c), CP_FLAG_##f, CP_FLAG_##f##_##s) +#define cp_wait(c, f, s) _cp_wait((c), CP_FLAG_##f, CP_FLAG_##f##_##s) static inline void _cp_set(struct nouveau_grctx *ctx, int flag, int state) { cp_out(ctx, CP_SET | flag | (state ? CP_SET_1 : 0)); } -#define cp_set(c,f,s) _cp_set((c), CP_FLAG_##f, CP_FLAG_##f##_##s) +#define cp_set(c, f, s) _cp_set((c), CP_FLAG_##f, CP_FLAG_##f##_##s) static inline void cp_pos(struct nouveau_grctx *ctx, int offset) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index c3e953b08992..2960f583dc38 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -51,8 +51,7 @@ nv10_mem_update_tile_region(struct drm_device *dev, struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - int i = tile - dev_priv->tile.reg; + int i = tile - dev_priv->tile.reg, j; unsigned long save; nouveau_fence_unref(&tile->fence); @@ -70,7 +69,10 @@ nv10_mem_update_tile_region(struct drm_device *dev, nouveau_wait_for_idle(dev); pfb->set_tile_region(dev, i); - pgraph->set_tile_region(dev, i); + for (j = 0; j < NVOBJ_ENGINE_NR; j++) { + if (dev_priv->eng[j] && dev_priv->eng[j]->set_tile_region) + dev_priv->eng[j]->set_tile_region(dev, i); + } pfifo->cache_pull(dev, true); pfifo->reassign(dev, true); @@ -595,10 +597,10 @@ nouveau_mem_timing_init(struct drm_device *dev) if (!memtimings->timing) return; - /* Get "some number" from the timing reg for NV_40 + /* Get "some number" from the timing reg for NV_40 and NV_50 * Used in calculations later */ - if(dev_priv->card_type == NV_40) { - magic_number = (nv_rd32(dev,0x100228) & 0x0f000000) >> 24; + if (dev_priv->card_type >= NV_40 && dev_priv->chipset < 0x98) { + magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24; } entry = mem + mem[1]; @@ -641,51 +643,68 @@ nouveau_mem_timing_init(struct drm_device *dev) /* XXX: I don't trust the -1's and +1's... they must come * from somewhere! */ timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 | - tUNK_18 << 16 | + max(tUNK_18, (u8) 1) << 16 | (tUNK_1 + tUNK_19 + 1 + magic_number) << 8; - if(dev_priv->chipset == 0xa8) { + if (dev_priv->chipset == 0xa8) { timing->reg_100224 |= (tUNK_2 - 1); } else { timing->reg_100224 |= (tUNK_2 + 2 - magic_number); } timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10); - if(dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa) { + if (dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa) timing->reg_100228 |= (tUNK_19 - 1) << 24; - } + else + timing->reg_100228 |= magic_number << 24; - if(dev_priv->card_type == NV_40) { + if (dev_priv->card_type == NV_40) { /* NV40: don't know what the rest of the regs are.. * And don't need to know either */ - timing->reg_100228 |= 0x20200000 | magic_number << 24; - } else if(dev_priv->card_type >= NV_50) { - /* XXX: reg_10022c */ - timing->reg_10022c = tUNK_2 - 1; + timing->reg_100228 |= 0x20200000; + } else if (dev_priv->card_type >= NV_50) { + if (dev_priv->chipset < 0x98 || + (dev_priv->chipset == 0x98 && + dev_priv->stepping <= 0xa1)) { + timing->reg_10022c = (0x14 + tUNK_2) << 24 | + 0x16 << 16 | + (tUNK_2 - 1) << 8 | + (tUNK_2 - 1); + } else { + /* XXX: reg_10022c for recentish cards */ + timing->reg_10022c = tUNK_2 - 1; + } timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 | tUNK_13 << 8 | tUNK_13); timing->reg_100234 = (tRAS << 24 | tRC); - timing->reg_100234 += max(tUNK_10,tUNK_11) << 16; + timing->reg_100234 += max(tUNK_10, tUNK_11) << 16; - if(dev_priv->chipset < 0xa3) { + if (dev_priv->chipset < 0x98 || + (dev_priv->chipset == 0x98 && + dev_priv->stepping <= 0xa1)) { timing->reg_100234 |= (tUNK_2 + 2) << 8; } else { /* XXX: +6? */ timing->reg_100234 |= (tUNK_19 + 6) << 8; } - /* XXX; reg_100238, reg_10023c - * reg_100238: 0x00?????? - * reg_10023c: 0x!!??0202 for NV50+ cards (empirical evidence) */ + /* XXX; reg_100238 + * reg_100238: 0x00?????? */ timing->reg_10023c = 0x202; - if(dev_priv->chipset < 0xa3) { + if (dev_priv->chipset < 0x98 || + (dev_priv->chipset == 0x98 && + dev_priv->stepping <= 0xa1)) { timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16; } else { - /* currently unknown + /* XXX: reg_10023c + * currently unknown * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */ } + + /* XXX: reg_100240? */ } + timing->id = i; NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i, timing->reg_100220, timing->reg_100224, @@ -693,10 +712,11 @@ nouveau_mem_timing_init(struct drm_device *dev) NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", timing->reg_100230, timing->reg_100234, timing->reg_100238, timing->reg_10023c); + NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240); } memtimings->nr_timing = entries; - memtimings->supported = true; + memtimings->supported = (dev_priv->chipset <= 0x98); } void diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 67a16e01ffa6..8f97016f5b26 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -361,20 +361,6 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, u32 pinst, u64 vinst, return 0; } - -static uint32_t -nouveau_gpuobj_class_instmem_size(struct drm_device *dev, int class) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - /*XXX: dodgy hack for now */ - if (dev_priv->card_type >= NV_50) - return 24; - if (dev_priv->card_type >= NV_40) - return 32; - return 16; -} - /* DMA objects are used to reference a piece of memory in the framebuffer, PCI or AGP address space. Each object is 16 bytes big @@ -606,11 +592,11 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, u64 base, set to 0? */ static int -nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class, - struct nouveau_gpuobj **gpuobj_ret) +nouveau_gpuobj_sw_new(struct nouveau_channel *chan, u32 handle, u16 class) { struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct nouveau_gpuobj *gpuobj; + int ret; gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL); if (!gpuobj) @@ -624,8 +610,10 @@ nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class, spin_lock(&dev_priv->ramin_lock); list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); spin_unlock(&dev_priv->ramin_lock); - *gpuobj_ret = gpuobj; - return 0; + + ret = nouveau_ramht_insert(chan, handle, gpuobj); + nouveau_gpuobj_ref(NULL, &gpuobj); + return ret; } int @@ -634,101 +622,30 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, u32 handle, int class) struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct drm_device *dev = chan->dev; struct nouveau_gpuobj_class *oc; - struct nouveau_gpuobj *gpuobj; int ret; NV_DEBUG(dev, "ch%d class=0x%04x\n", chan->id, class); list_for_each_entry(oc, &dev_priv->classes, head) { - if (oc->id == class) - goto found; - } - - NV_ERROR(dev, "illegal object class: 0x%x\n", class); - return -EINVAL; + struct nouveau_exec_engine *eng = dev_priv->eng[oc->engine]; -found: - switch (oc->engine) { - case NVOBJ_ENGINE_SW: - if (dev_priv->card_type < NV_C0) { - ret = nouveau_gpuobj_sw_new(chan, class, &gpuobj); - if (ret) - return ret; - goto insert; - } - break; - case NVOBJ_ENGINE_GR: - if ((dev_priv->card_type >= NV_20 && !chan->ramin_grctx) || - (dev_priv->card_type < NV_20 && !chan->pgraph_ctx)) { - struct nouveau_pgraph_engine *pgraph = - &dev_priv->engine.graph; + if (oc->id != class) + continue; - ret = pgraph->create_context(chan); - if (ret) - return ret; - } - break; - case NVOBJ_ENGINE_CRYPT: - if (!chan->crypt_ctx) { - struct nouveau_crypt_engine *pcrypt = - &dev_priv->engine.crypt; + if (oc->engine == NVOBJ_ENGINE_SW) + return nouveau_gpuobj_sw_new(chan, handle, class); - ret = pcrypt->create_context(chan); + if (!chan->engctx[oc->engine]) { + ret = eng->context_new(chan, oc->engine); if (ret) return ret; } - break; - } - - /* we're done if this is fermi */ - if (dev_priv->card_type >= NV_C0) - return 0; - - ret = nouveau_gpuobj_new(dev, chan, - nouveau_gpuobj_class_instmem_size(dev, class), - 16, - NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE, - &gpuobj); - if (ret) { - NV_ERROR(dev, "error creating gpuobj: %d\n", ret); - return ret; - } - if (dev_priv->card_type >= NV_50) { - nv_wo32(gpuobj, 0, class); - nv_wo32(gpuobj, 20, 0x00010000); - } else { - switch (class) { - case NV_CLASS_NULL: - nv_wo32(gpuobj, 0, 0x00001030); - nv_wo32(gpuobj, 4, 0xFFFFFFFF); - break; - default: - if (dev_priv->card_type >= NV_40) { - nv_wo32(gpuobj, 0, class); -#ifdef __BIG_ENDIAN - nv_wo32(gpuobj, 8, 0x01000000); -#endif - } else { -#ifdef __BIG_ENDIAN - nv_wo32(gpuobj, 0, class | 0x00080000); -#else - nv_wo32(gpuobj, 0, class); -#endif - } - } + return eng->object_new(chan, oc->engine, handle, class); } - dev_priv->engine.instmem.flush(dev); - - gpuobj->engine = oc->engine; - gpuobj->class = oc->id; -insert: - ret = nouveau_ramht_insert(chan, handle, gpuobj); - if (ret) - NV_ERROR(dev, "error adding gpuobj to RAMHT: %d\n", ret); - nouveau_gpuobj_ref(NULL, &gpuobj); - return ret; + NV_ERROR(dev, "illegal object class: 0x%x\n", class); + return -EINVAL; } static int @@ -746,9 +663,6 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) size = 0x2000; base = 0; - /* PGRAPH context */ - size += dev_priv->engine.graph.grctx_size; - if (dev_priv->card_type == NV_50) { /* Various fixed table thingos */ size += 0x1400; /* mostly unknown stuff */ diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 670e3cb697ec..922fb6b664ed 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -72,6 +72,68 @@ legacy_perf_init(struct drm_device *dev) pm->nr_perflvl = 1; } +static struct nouveau_pm_memtiming * +nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, + u16 memclk, u8 *entry, u8 recordlen, u8 entries) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct nvbios *bios = &dev_priv->vbios; + u8 ramcfg; + int i; + + /* perf v2 has a separate "timing map" table, we have to match + * the target memory clock to a specific entry, *then* use + * ramcfg to select the correct subentry + */ + if (P->version == 2) { + u8 *tmap = ROMPTR(bios, P->data[4]); + if (!tmap) { + NV_DEBUG(dev, "no timing map pointer\n"); + return NULL; + } + + if (tmap[0] != 0x10) { + NV_WARN(dev, "timing map 0x%02x unknown\n", tmap[0]); + return NULL; + } + + entry = tmap + tmap[1]; + recordlen = tmap[2] + (tmap[4] * tmap[3]); + for (i = 0; i < tmap[5]; i++, entry += recordlen) { + if (memclk >= ROM16(entry[0]) && + memclk <= ROM16(entry[2])) + break; + } + + if (i == tmap[5]) { + NV_WARN(dev, "no match in timing map table\n"); + return NULL; + } + + entry += tmap[2]; + recordlen = tmap[3]; + entries = tmap[4]; + } + + ramcfg = (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2; + if (bios->ram_restrict_tbl_ptr) + ramcfg = bios->data[bios->ram_restrict_tbl_ptr + ramcfg]; + + if (ramcfg >= entries) { + NV_WARN(dev, "ramcfg strap out of bounds!\n"); + return NULL; + } + + entry += ramcfg * recordlen; + if (entry[1] >= pm->memtimings.nr_timing) { + NV_WARN(dev, "timingset %d does not exist\n", entry[1]); + return NULL; + } + + return &pm->memtimings.timing[entry[1]]; +} + void nouveau_perf_init(struct drm_device *dev) { @@ -124,6 +186,8 @@ nouveau_perf_init(struct drm_device *dev) for (i = 0; i < entries; i++) { struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; + perflvl->timing = NULL; + if (entry[0] == 0xff) { entry += recordlen; continue; @@ -174,9 +238,21 @@ nouveau_perf_init(struct drm_device *dev) #define subent(n) entry[perf[2] + ((n) * perf[3])] perflvl->fanspeed = 0; /*XXX*/ perflvl->voltage = entry[2]; - perflvl->core = (ROM16(subent(0)) & 0xfff) * 1000; - perflvl->shader = (ROM16(subent(1)) & 0xfff) * 1000; - perflvl->memory = (ROM16(subent(2)) & 0xfff) * 1000; + if (dev_priv->card_type == NV_50) { + perflvl->core = ROM16(subent(0)) & 0xfff; + perflvl->shader = ROM16(subent(1)) & 0xfff; + perflvl->memory = ROM16(subent(2)) & 0xfff; + } else { + perflvl->shader = ROM16(subent(3)) & 0xfff; + perflvl->core = perflvl->shader / 2; + perflvl->unk0a = ROM16(subent(4)) & 0xfff; + perflvl->memory = ROM16(subent(5)) & 0xfff; + } + + perflvl->core *= 1000; + perflvl->shader *= 1000; + perflvl->memory *= 1000; + perflvl->unk0a *= 1000; break; } @@ -190,6 +266,16 @@ nouveau_perf_init(struct drm_device *dev) } } + /* get the corresponding memory timings */ + if (version > 0x15) { + /* last 3 args are for < 0x40, ignored for >= 0x40 */ + perflvl->timing = + nouveau_perf_timing(dev, &P, + perflvl->memory / 1000, + entry + perf[3], + perf[5], perf[4]); + } + snprintf(perflvl->name, sizeof(perflvl->name), "performance_level_%d", i); perflvl->id = i; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 4399e2f34db4..da8d994d5e8a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -156,7 +156,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) static void nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) { - char c[16], s[16], v[16], f[16]; + char c[16], s[16], v[16], f[16], t[16]; c[0] = '\0'; if (perflvl->core) @@ -174,8 +174,12 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) if (perflvl->fanspeed) snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); - snprintf(ptr, len, "memory %dMHz%s%s%s%s\n", perflvl->memory / 1000, - c, s, v, f); + t[0] = '\0'; + if (perflvl->timing) + snprintf(t, sizeof(t), " timing %d", perflvl->timing->id); + + snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000, + c, s, v, f, t); } static ssize_t @@ -449,7 +453,7 @@ nouveau_hwmon_fini(struct drm_device *dev) #endif } -#ifdef CONFIG_ACPI +#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) static int nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data) { @@ -476,10 +480,10 @@ nouveau_pm_init(struct drm_device *dev) char info[256]; int ret, i; + nouveau_mem_timing_init(dev); nouveau_volt_init(dev); nouveau_perf_init(dev); nouveau_temp_init(dev); - nouveau_mem_timing_init(dev); NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { @@ -490,6 +494,7 @@ nouveau_pm_init(struct drm_device *dev) /* determine current ("boot") performance level */ ret = nouveau_pm_perflvl_get(dev, &pm->boot); if (ret == 0) { + strncpy(pm->boot.name, "boot", 4); pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); @@ -507,7 +512,7 @@ nouveau_pm_init(struct drm_device *dev) nouveau_sysfs_init(dev); nouveau_hwmon_init(dev); -#ifdef CONFIG_ACPI +#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) pm->acpi_nb.notifier_call = nouveau_pm_acpi_event; register_acpi_notifier(&pm->acpi_nb); #endif @@ -524,12 +529,12 @@ nouveau_pm_fini(struct drm_device *dev) if (pm->cur != &pm->boot) nouveau_pm_perflvl_set(dev, &pm->boot); - nouveau_mem_timing_fini(dev); nouveau_temp_fini(dev); nouveau_perf_fini(dev); nouveau_volt_fini(dev); + nouveau_mem_timing_fini(dev); -#ifdef CONFIG_ACPI +#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) unregister_acpi_notifier(&pm->acpi_nb); #endif nouveau_hwmon_fini(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index 04e8fb795269..f18cdfc3400f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -639,9 +639,9 @@ # define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240 # define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258 -#define NV50_AUXCH_DATA_OUT(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0) +#define NV50_AUXCH_DATA_OUT(i, n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0) #define NV50_AUXCH_DATA_OUT__SIZE 4 -#define NV50_AUXCH_DATA_IN(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0) +#define NV50_AUXCH_DATA_IN(i, n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0) #define NV50_AUXCH_DATA_IN__SIZE 4 #define NV50_AUXCH_ADDR(i) ((i) * 0x50 + 0x0000e4e0) #define NV50_AUXCH_CTRL(i) ((i) * 0x50 + 0x0000e4e4) @@ -829,7 +829,7 @@ #define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084 #define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000 #define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff -#define NV50_SOR_DP_CTRL(i,l) (0x0061c10c + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_CTRL_ENABLED 0x00000001 #define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000 #define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000 @@ -841,10 +841,10 @@ #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED 0x00000000 #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1 0x01000000 #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 -#define NV50_SOR_DP_UNK118(i,l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) -#define NV50_SOR_DP_UNK120(i,l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) -#define NV50_SOR_DP_UNK128(i,l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) -#define NV50_SOR_DP_UNK130(i,l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) #define NV50_PDISPLAY_USER_PUT(i) ((i) * 0x1000 + 0x00640000) diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 915fbce89595..38ea662568c1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -65,14 +65,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nv04_fb_init; engine->fb.takedown = nv04_fb_takedown; - engine->graph.init = nv04_graph_init; - engine->graph.takedown = nv04_graph_takedown; - engine->graph.fifo_access = nv04_graph_fifo_access; - engine->graph.channel = nv04_graph_channel; - engine->graph.create_context = nv04_graph_create_context; - engine->graph.destroy_context = nv04_graph_destroy_context; - engine->graph.load_context = nv04_graph_load_context; - engine->graph.unload_context = nv04_graph_unload_context; engine->fifo.channels = 16; engine->fifo.init = nv04_fifo_init; engine->fifo.takedown = nv04_fifo_fini; @@ -98,8 +90,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_get = nv04_pm_clock_get; engine->pm.clock_pre = nv04_pm_clock_pre; engine->pm.clock_set = nv04_pm_clock_set; - engine->crypt.init = nouveau_stub_init; - engine->crypt.takedown = nouveau_stub_takedown; engine->vram.init = nouveau_mem_detect; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -123,15 +113,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv10_fb_init_tile_region; engine->fb.set_tile_region = nv10_fb_set_tile_region; engine->fb.free_tile_region = nv10_fb_free_tile_region; - engine->graph.init = nv10_graph_init; - engine->graph.takedown = nv10_graph_takedown; - engine->graph.channel = nv10_graph_channel; - engine->graph.create_context = nv10_graph_create_context; - engine->graph.destroy_context = nv10_graph_destroy_context; - engine->graph.fifo_access = nv04_graph_fifo_access; - engine->graph.load_context = nv10_graph_load_context; - engine->graph.unload_context = nv10_graph_unload_context; - engine->graph.set_tile_region = nv10_graph_set_tile_region; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nv04_fifo_fini; @@ -157,8 +138,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_get = nv04_pm_clock_get; engine->pm.clock_pre = nv04_pm_clock_pre; engine->pm.clock_set = nv04_pm_clock_set; - engine->crypt.init = nouveau_stub_init; - engine->crypt.takedown = nouveau_stub_takedown; engine->vram.init = nouveau_mem_detect; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -182,15 +161,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv10_fb_init_tile_region; engine->fb.set_tile_region = nv10_fb_set_tile_region; engine->fb.free_tile_region = nv10_fb_free_tile_region; - engine->graph.init = nv20_graph_init; - engine->graph.takedown = nv20_graph_takedown; - engine->graph.channel = nv10_graph_channel; - engine->graph.create_context = nv20_graph_create_context; - engine->graph.destroy_context = nv20_graph_destroy_context; - engine->graph.fifo_access = nv04_graph_fifo_access; - engine->graph.load_context = nv20_graph_load_context; - engine->graph.unload_context = nv20_graph_unload_context; - engine->graph.set_tile_region = nv20_graph_set_tile_region; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nv04_fifo_fini; @@ -216,8 +186,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_get = nv04_pm_clock_get; engine->pm.clock_pre = nv04_pm_clock_pre; engine->pm.clock_set = nv04_pm_clock_set; - engine->crypt.init = nouveau_stub_init; - engine->crypt.takedown = nouveau_stub_takedown; engine->vram.init = nouveau_mem_detect; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -241,15 +209,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv30_fb_init_tile_region; engine->fb.set_tile_region = nv10_fb_set_tile_region; engine->fb.free_tile_region = nv30_fb_free_tile_region; - engine->graph.init = nv30_graph_init; - engine->graph.takedown = nv20_graph_takedown; - engine->graph.fifo_access = nv04_graph_fifo_access; - engine->graph.channel = nv10_graph_channel; - engine->graph.create_context = nv20_graph_create_context; - engine->graph.destroy_context = nv20_graph_destroy_context; - engine->graph.load_context = nv20_graph_load_context; - engine->graph.unload_context = nv20_graph_unload_context; - engine->graph.set_tile_region = nv20_graph_set_tile_region; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nv04_fifo_fini; @@ -277,8 +236,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_set = nv04_pm_clock_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; - engine->crypt.init = nouveau_stub_init; - engine->crypt.takedown = nouveau_stub_takedown; engine->vram.init = nouveau_mem_detect; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -303,15 +260,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv30_fb_init_tile_region; engine->fb.set_tile_region = nv40_fb_set_tile_region; engine->fb.free_tile_region = nv30_fb_free_tile_region; - engine->graph.init = nv40_graph_init; - engine->graph.takedown = nv40_graph_takedown; - engine->graph.fifo_access = nv04_graph_fifo_access; - engine->graph.channel = nv40_graph_channel; - engine->graph.create_context = nv40_graph_create_context; - engine->graph.destroy_context = nv40_graph_destroy_context; - engine->graph.load_context = nv40_graph_load_context; - engine->graph.unload_context = nv40_graph_unload_context; - engine->graph.set_tile_region = nv40_graph_set_tile_region; engine->fifo.channels = 32; engine->fifo.init = nv40_fifo_init; engine->fifo.takedown = nv04_fifo_fini; @@ -340,8 +288,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->pm.temp_get = nv40_temp_get; - engine->crypt.init = nouveau_stub_init; - engine->crypt.takedown = nouveau_stub_takedown; engine->vram.init = nouveau_mem_detect; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -368,19 +314,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nv50_fb_init; engine->fb.takedown = nv50_fb_takedown; - engine->graph.init = nv50_graph_init; - engine->graph.takedown = nv50_graph_takedown; - engine->graph.fifo_access = nv50_graph_fifo_access; - engine->graph.channel = nv50_graph_channel; - engine->graph.create_context = nv50_graph_create_context; - engine->graph.destroy_context = nv50_graph_destroy_context; - engine->graph.load_context = nv50_graph_load_context; - engine->graph.unload_context = nv50_graph_unload_context; - if (dev_priv->chipset == 0x50 || - dev_priv->chipset == 0xac) - engine->graph.tlb_flush = nv50_graph_tlb_flush; - else - engine->graph.tlb_flush = nv84_graph_tlb_flush; engine->fifo.channels = 128; engine->fifo.init = nv50_fifo_init; engine->fifo.takedown = nv50_fifo_takedown; @@ -432,24 +365,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv84_temp_get; else engine->pm.temp_get = nv40_temp_get; - switch (dev_priv->chipset) { - case 0x84: - case 0x86: - case 0x92: - case 0x94: - case 0x96: - case 0xa0: - engine->crypt.init = nv84_crypt_init; - engine->crypt.takedown = nv84_crypt_fini; - engine->crypt.create_context = nv84_crypt_create_context; - engine->crypt.destroy_context = nv84_crypt_destroy_context; - engine->crypt.tlb_flush = nv84_crypt_tlb_flush; - break; - default: - engine->crypt.init = nouveau_stub_init; - engine->crypt.takedown = nouveau_stub_takedown; - break; - } engine->vram.init = nv50_vram_init; engine->vram.get = nv50_vram_new; engine->vram.put = nv50_vram_del; @@ -472,14 +387,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nvc0_fb_init; engine->fb.takedown = nvc0_fb_takedown; - engine->graph.init = nvc0_graph_init; - engine->graph.takedown = nvc0_graph_takedown; - engine->graph.fifo_access = nvc0_graph_fifo_access; - engine->graph.channel = nvc0_graph_channel; - engine->graph.create_context = nvc0_graph_create_context; - engine->graph.destroy_context = nvc0_graph_destroy_context; - engine->graph.load_context = nvc0_graph_load_context; - engine->graph.unload_context = nvc0_graph_unload_context; engine->fifo.channels = 128; engine->fifo.init = nvc0_fifo_init; engine->fifo.takedown = nvc0_fifo_takedown; @@ -503,8 +410,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->gpio.irq_register = nv50_gpio_irq_register; engine->gpio.irq_unregister = nv50_gpio_irq_unregister; engine->gpio.irq_enable = nv50_gpio_irq_enable; - engine->crypt.init = nouveau_stub_init; - engine->crypt.takedown = nouveau_stub_takedown; engine->vram.init = nvc0_vram_init; engine->vram.get = nvc0_vram_new; engine->vram.put = nv50_vram_del; @@ -593,7 +498,7 @@ nouveau_card_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_engine *engine; - int ret; + int ret, e = 0; vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state, @@ -658,23 +563,80 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_timer; - if (nouveau_noaccel) - engine->graph.accel_blocked = true; - else { - /* PGRAPH */ - ret = engine->graph.init(dev); - if (ret) - goto out_fb; + switch (dev_priv->card_type) { + case NV_04: + nv04_graph_create(dev); + break; + case NV_10: + nv10_graph_create(dev); + break; + case NV_20: + case NV_30: + nv20_graph_create(dev); + break; + case NV_40: + nv40_graph_create(dev); + break; + case NV_50: + nv50_graph_create(dev); + break; + case NV_C0: + nvc0_graph_create(dev); + break; + default: + break; + } - /* PCRYPT */ - ret = engine->crypt.init(dev); - if (ret) - goto out_graph; + switch (dev_priv->chipset) { + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0xa0: + nv84_crypt_create(dev); + break; + } + + switch (dev_priv->card_type) { + case NV_50: + switch (dev_priv->chipset) { + case 0xa3: + case 0xa5: + case 0xa8: + case 0xaf: + nva3_copy_create(dev); + break; + } + break; + case NV_C0: + nvc0_copy_create(dev, 0); + nvc0_copy_create(dev, 1); + break; + default: + break; + } + + if (dev_priv->card_type == NV_40) + nv40_mpeg_create(dev); + else + if (dev_priv->card_type == NV_50 && + (dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0)) + nv50_mpeg_create(dev); + + if (!nouveau_noaccel) { + for (e = 0; e < NVOBJ_ENGINE_NR; e++) { + if (dev_priv->eng[e]) { + ret = dev_priv->eng[e]->init(dev, e); + if (ret) + goto out_engine; + } + } /* PFIFO */ ret = engine->fifo.init(dev); if (ret) - goto out_crypt; + goto out_engine; } ret = engine->display.create(dev); @@ -691,7 +653,7 @@ nouveau_card_init(struct drm_device *dev) /* what about PVIDEO/PCRTC/PRAMDAC etc? */ - if (!engine->graph.accel_blocked) { + if (dev_priv->eng[NVOBJ_ENGINE_GR]) { ret = nouveau_fence_init(dev); if (ret) goto out_irq; @@ -715,13 +677,16 @@ out_vblank: out_fifo: if (!nouveau_noaccel) engine->fifo.takedown(dev); -out_crypt: - if (!nouveau_noaccel) - engine->crypt.takedown(dev); -out_graph: - if (!nouveau_noaccel) - engine->graph.takedown(dev); -out_fb: +out_engine: + if (!nouveau_noaccel) { + for (e = e - 1; e >= 0; e--) { + if (!dev_priv->eng[e]) + continue; + dev_priv->eng[e]->fini(dev, e); + dev_priv->eng[e]->destroy(dev,e ); + } + } + engine->fb.takedown(dev); out_timer: engine->timer.takedown(dev); @@ -751,16 +716,21 @@ static void nouveau_card_takedown(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_engine *engine = &dev_priv->engine; + int e; - if (!engine->graph.accel_blocked) { + if (dev_priv->channel) { nouveau_fence_fini(dev); nouveau_channel_put_unlocked(&dev_priv->channel); } if (!nouveau_noaccel) { engine->fifo.takedown(dev); - engine->crypt.takedown(dev); - engine->graph.takedown(dev); + for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) { + if (dev_priv->eng[e]) { + dev_priv->eng[e]->fini(dev, e); + dev_priv->eng[e]->destroy(dev,e ); + } + } } engine->fb.takedown(dev); engine->timer.takedown(dev); @@ -866,7 +836,7 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev) #ifdef CONFIG_X86 primary = dev->pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; #endif - + remove_conflicting_framebuffers(dev_priv->apertures, "nouveaufb", primary); return 0; } @@ -918,11 +888,13 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) /* Time to determine the card architecture */ reg0 = nv_rd32(dev, NV03_PMC_BOOT_0); + dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */ /* We're dealing with >=NV10 */ if ((reg0 & 0x0f000000) > 0) { /* Bit 27-20 contain the architecture in hex */ dev_priv->chipset = (reg0 & 0xff00000) >> 20; + dev_priv->stepping = (reg0 & 0xff); /* NV04 or NV05 */ } else if ((reg0 & 0xff00fff0) == 0x20004000) { if (reg0 & 0x00f00000) diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h index 2e06b55cfdc1..c48a9fc2b47b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.h +++ b/drivers/gpu/drm/nouveau/nouveau_vm.h @@ -53,8 +53,7 @@ struct nouveau_vm { int refcount; struct list_head pgd_list; - atomic_t pgraph_refs; - atomic_t pcrypt_refs; + atomic_t engref[16]; struct nouveau_vm_pgt *pgt; u32 fpde; diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 04fdc00a67d5..75e872741d92 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -159,8 +159,16 @@ nouveau_volt_init(struct drm_device *dev) headerlen = volt[1]; recordlen = volt[2]; entries = volt[3]; - vidshift = hweight8(volt[5]); vidmask = volt[4]; + /* no longer certain what volt[5] is, if it's related to + * the vid shift then it's definitely not a function of + * how many bits are set. + * + * after looking at a number of nva3+ vbios images, they + * all seem likely to have a static shift of 2.. lets + * go with that for now until proven otherwise. + */ + vidshift = 2; break; default: NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]); diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index 748b9d9c2949..3c78bc81357e 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -790,8 +790,7 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, if (atomic) { drm_fb = passed_fb; fb = nouveau_framebuffer(passed_fb); - } - else { + } else { /* If not atomic, we can go ahead and pin, and unpin the * old fb we were passed. */ @@ -944,14 +943,14 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, struct drm_gem_object *gem; int ret = 0; - if (width != 64 || height != 64) - return -EINVAL; - if (!buffer_handle) { nv_crtc->cursor.hide(nv_crtc, true); return 0; } + if (width != 64 || height != 64) + return -EINVAL; + gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); if (!gem) return -ENOENT; diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c index af75015068d6..3626ee7db3ba 100644 --- a/drivers/gpu/drm/nouveau/nv04_graph.c +++ b/drivers/gpu/drm/nouveau/nv04_graph.c @@ -28,9 +28,11 @@ #include "nouveau_drv.h" #include "nouveau_hw.h" #include "nouveau_util.h" +#include "nouveau_ramht.h" -static int nv04_graph_register(struct drm_device *dev); -static void nv04_graph_isr(struct drm_device *dev); +struct nv04_graph_engine { + struct nouveau_exec_engine base; +}; static uint32_t nv04_graph_ctx_regs[] = { 0x0040053c, @@ -350,7 +352,7 @@ struct graph_state { uint32_t nv04[ARRAY_SIZE(nv04_graph_ctx_regs)]; }; -struct nouveau_channel * +static struct nouveau_channel * nv04_graph_channel(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -365,26 +367,6 @@ nv04_graph_channel(struct drm_device *dev) return dev_priv->channels.ptr[chid]; } -static void -nv04_graph_context_switch(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nouveau_channel *chan = NULL; - int chid; - - nouveau_wait_for_idle(dev); - - /* If previous context is valid, we need to save it */ - pgraph->unload_context(dev); - - /* Load context for next channel */ - chid = dev_priv->engine.fifo.channel_id(dev); - chan = dev_priv->channels.ptr[chid]; - if (chan) - nv04_graph_load_context(chan); -} - static uint32_t *ctx_reg(struct graph_state *ctx, uint32_t reg) { int i; @@ -397,48 +379,11 @@ static uint32_t *ctx_reg(struct graph_state *ctx, uint32_t reg) return NULL; } -int nv04_graph_create_context(struct nouveau_channel *chan) -{ - struct graph_state *pgraph_ctx; - NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id); - - chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), - GFP_KERNEL); - if (pgraph_ctx == NULL) - return -ENOMEM; - - *ctx_reg(pgraph_ctx, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31; - - return 0; -} - -void nv04_graph_destroy_context(struct nouveau_channel *chan) -{ - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct graph_state *pgraph_ctx = chan->pgraph_ctx; - unsigned long flags; - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - pgraph->fifo_access(dev, false); - - /* Unload the context if it's the currently active one */ - if (pgraph->channel(dev) == chan) - pgraph->unload_context(dev); - - /* Free the context resources */ - kfree(pgraph_ctx); - chan->pgraph_ctx = NULL; - - pgraph->fifo_access(dev, true); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); -} - -int nv04_graph_load_context(struct nouveau_channel *chan) +static int +nv04_graph_load_context(struct nouveau_channel *chan) { + struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR]; struct drm_device *dev = chan->dev; - struct graph_state *pgraph_ctx = chan->pgraph_ctx; uint32_t tmp; int i; @@ -456,20 +401,19 @@ int nv04_graph_load_context(struct nouveau_channel *chan) return 0; } -int +static int nv04_graph_unload_context(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; struct nouveau_channel *chan = NULL; struct graph_state *ctx; uint32_t tmp; int i; - chan = pgraph->channel(dev); + chan = nv04_graph_channel(dev); if (!chan) return 0; - ctx = chan->pgraph_ctx; + ctx = chan->engctx[NVOBJ_ENGINE_GR]; for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++) ctx->nv04[i] = nv_rd32(dev, nv04_graph_ctx_regs[i]); @@ -481,23 +425,85 @@ nv04_graph_unload_context(struct drm_device *dev) return 0; } -int nv04_graph_init(struct drm_device *dev) +static int +nv04_graph_context_new(struct nouveau_channel *chan, int engine) { + struct graph_state *pgraph_ctx; + NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id); + + pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), GFP_KERNEL); + if (pgraph_ctx == NULL) + return -ENOMEM; + + *ctx_reg(pgraph_ctx, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31; + + chan->engctx[engine] = pgraph_ctx; + return 0; +} + +static void +nv04_graph_context_del(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t tmp; + struct graph_state *pgraph_ctx = chan->engctx[engine]; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv04_graph_fifo_access(dev, false); + + /* Unload the context if it's the currently active one */ + if (nv04_graph_channel(dev) == chan) + nv04_graph_unload_context(dev); + + nv04_graph_fifo_access(dev, true); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + /* Free the context resources */ + kfree(pgraph_ctx); + chan->engctx[engine] = NULL; +} + +int +nv04_graph_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct drm_device *dev = chan->dev; + struct nouveau_gpuobj *obj = NULL; int ret; + ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj); + if (ret) + return ret; + obj->engine = 1; + obj->class = class; + +#ifdef __BIG_ENDIAN + nv_wo32(obj, 0x00, 0x00080000 | class); +#else + nv_wo32(obj, 0x00, class); +#endif + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; +} + +static int +nv04_graph_init(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t tmp; + nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH); nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); - ret = nv04_graph_register(dev); - if (ret) - return ret; - /* Enable PGRAPH interrupts */ - nouveau_irq_register(dev, 12, nv04_graph_isr); nv_wr32(dev, NV03_PGRAPH_INTR, 0xFFFFFFFF); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); @@ -507,7 +513,7 @@ int nv04_graph_init(struct drm_device *dev) nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x1231c000); /*1231C000 blob, 001 haiku*/ - //*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/ + /*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x72111100); /*0x72111100 blob , 01 haiku*/ /*nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/ @@ -531,10 +537,12 @@ int nv04_graph_init(struct drm_device *dev) return 0; } -void nv04_graph_takedown(struct drm_device *dev) +static int +nv04_graph_fini(struct drm_device *dev, int engine) { + nv04_graph_unload_context(dev); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000); - nouveau_irq_unregister(dev, 12); + return 0; } void @@ -969,13 +977,138 @@ nv04_graph_mthd_bind_chroma(struct nouveau_channel *chan, return 1; } -static int -nv04_graph_register(struct drm_device *dev) +static struct nouveau_bitfield nv04_graph_intr[] = { + { NV_PGRAPH_INTR_NOTIFY, "NOTIFY" }, + {} +}; + +static struct nouveau_bitfield nv04_graph_nstatus[] = { + { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" }, + { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" }, + { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" }, + { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }, + {} +}; + +struct nouveau_bitfield nv04_graph_nsource[] = { + { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" }, + { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" }, + { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" }, + { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" }, + { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" }, + { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" }, + { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" }, + { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" }, + { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" }, + { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" }, + { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" }, + { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" }, + { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" }, + { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" }, + { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" }, + { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" }, + { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" }, + { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" }, + { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" }, + {} +}; + +static void +nv04_graph_context_switch(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_channel *chan = NULL; + int chid; - if (dev_priv->engine.graph.registered) - return 0; + nouveau_wait_for_idle(dev); + + /* If previous context is valid, we need to save it */ + nv04_graph_unload_context(dev); + + /* Load context for next channel */ + chid = dev_priv->engine.fifo.channel_id(dev); + chan = dev_priv->channels.ptr[chid]; + if (chan) + nv04_graph_load_context(chan); +} + +static void +nv04_graph_isr(struct drm_device *dev) +{ + u32 stat; + + while ((stat = nv_rd32(dev, NV03_PGRAPH_INTR))) { + u32 nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE); + u32 nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS); + u32 addr = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR); + u32 chid = (addr & 0x0f000000) >> 24; + u32 subc = (addr & 0x0000e000) >> 13; + u32 mthd = (addr & 0x00001ffc); + u32 data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nv_rd32(dev, 0x400180 + subc * 4) & 0xff; + u32 show = stat; + + if (stat & NV_PGRAPH_INTR_NOTIFY) { + if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) { + if (!nouveau_gpuobj_mthd_call2(dev, chid, class, mthd, data)) + show &= ~NV_PGRAPH_INTR_NOTIFY; + } + } + + if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) { + nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); + stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + nv04_graph_context_switch(dev); + } + + nv_wr32(dev, NV03_PGRAPH_INTR, stat); + nv_wr32(dev, NV04_PGRAPH_FIFO, 0x00000001); + + if (show && nouveau_ratelimit()) { + NV_INFO(dev, "PGRAPH -"); + nouveau_bitfield_print(nv04_graph_intr, show); + printk(" nsource:"); + nouveau_bitfield_print(nv04_graph_nsource, nsource); + printk(" nstatus:"); + nouveau_bitfield_print(nv04_graph_nstatus, nstatus); + printk("\n"); + NV_INFO(dev, "PGRAPH - ch %d/%d class 0x%04x " + "mthd 0x%04x data 0x%08x\n", + chid, subc, class, mthd, data); + } + } +} + +static void +nv04_graph_destroy(struct drm_device *dev, int engine) +{ + struct nv04_graph_engine *pgraph = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 12); + + NVOBJ_ENGINE_DEL(dev, GR); + kfree(pgraph); +} + +int +nv04_graph_create(struct drm_device *dev) +{ + struct nv04_graph_engine *pgraph; + + pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL); + if (!pgraph) + return -ENOMEM; + + pgraph->base.destroy = nv04_graph_destroy; + pgraph->base.init = nv04_graph_init; + pgraph->base.fini = nv04_graph_fini; + pgraph->base.context_new = nv04_graph_context_new; + pgraph->base.context_del = nv04_graph_context_del; + pgraph->base.object_new = nv04_graph_object_new; + + NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); + nouveau_irq_register(dev, 12, nv04_graph_isr); /* dvd subpicture */ NVOBJ_CLASS(dev, 0x0038, GR); @@ -1222,93 +1355,5 @@ nv04_graph_register(struct drm_device *dev) NVOBJ_CLASS(dev, 0x506e, SW); NVOBJ_MTHD (dev, 0x506e, 0x0150, nv04_graph_mthd_set_ref); NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); - - dev_priv->engine.graph.registered = true; return 0; -}; - -static struct nouveau_bitfield nv04_graph_intr[] = { - { NV_PGRAPH_INTR_NOTIFY, "NOTIFY" }, - {} -}; - -static struct nouveau_bitfield nv04_graph_nstatus[] = -{ - { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" }, - { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" }, - { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" }, - { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }, - {} -}; - -struct nouveau_bitfield nv04_graph_nsource[] = -{ - { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" }, - { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" }, - { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" }, - { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" }, - { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" }, - { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" }, - { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" }, - { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" }, - { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" }, - { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" }, - { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" }, - { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" }, - { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" }, - { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" }, - { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" }, - { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" }, - { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" }, - { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" }, - { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" }, - {} -}; - -static void -nv04_graph_isr(struct drm_device *dev) -{ - u32 stat; - - while ((stat = nv_rd32(dev, NV03_PGRAPH_INTR))) { - u32 nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE); - u32 nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS); - u32 addr = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR); - u32 chid = (addr & 0x0f000000) >> 24; - u32 subc = (addr & 0x0000e000) >> 13; - u32 mthd = (addr & 0x00001ffc); - u32 data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA); - u32 class = nv_rd32(dev, 0x400180 + subc * 4) & 0xff; - u32 show = stat; - - if (stat & NV_PGRAPH_INTR_NOTIFY) { - if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) { - if (!nouveau_gpuobj_mthd_call2(dev, chid, class, mthd, data)) - show &= ~NV_PGRAPH_INTR_NOTIFY; - } - } - - if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) { - nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); - stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; - show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; - nv04_graph_context_switch(dev); - } - - nv_wr32(dev, NV03_PGRAPH_INTR, stat); - nv_wr32(dev, NV04_PGRAPH_FIFO, 0x00000001); - - if (show && nouveau_ratelimit()) { - NV_INFO(dev, "PGRAPH -"); - nouveau_bitfield_print(nv04_graph_intr, show); - printk(" nsource:"); - nouveau_bitfield_print(nv04_graph_nsource, nsource); - printk(" nstatus:"); - nouveau_bitfield_print(nv04_graph_nstatus, nstatus); - printk("\n"); - NV_INFO(dev, "PGRAPH - ch %d/%d class 0x%04x " - "mthd 0x%04x data 0x%08x\n", - chid, subc, class, mthd, data); - } - } } diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c index b8e3edb5c063..b8611b955313 100644 --- a/drivers/gpu/drm/nouveau/nv04_instmem.c +++ b/drivers/gpu/drm/nouveau/nv04_instmem.c @@ -95,6 +95,9 @@ nv04_instmem_takedown(struct drm_device *dev) nouveau_ramht_ref(NULL, &dev_priv->ramht, NULL); nouveau_gpuobj_ref(NULL, &dev_priv->ramro); nouveau_gpuobj_ref(NULL, &dev_priv->ramfc); + + if (drm_mm_initialized(&dev_priv->ramin_heap)) + drm_mm_takedown(&dev_priv->ramin_heap); } int diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c index 8c92edb7bbcd..0930c6cb88e0 100644 --- a/drivers/gpu/drm/nouveau/nv10_graph.c +++ b/drivers/gpu/drm/nouveau/nv10_graph.c @@ -28,10 +28,9 @@ #include "nouveau_drv.h" #include "nouveau_util.h" -static int nv10_graph_register(struct drm_device *); -static void nv10_graph_isr(struct drm_device *); - -#define NV10_FIFO_NUMBER 32 +struct nv10_graph_engine { + struct nouveau_exec_engine base; +}; struct pipe_state { uint32_t pipe_0x0000[0x040/4]; @@ -414,9 +413,9 @@ struct graph_state { static void nv10_graph_save_pipe(struct nouveau_channel *chan) { - struct drm_device *dev = chan->dev; - struct graph_state *pgraph_ctx = chan->pgraph_ctx; + struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR]; struct pipe_state *pipe = &pgraph_ctx->pipe_state; + struct drm_device *dev = chan->dev; PIPE_SAVE(dev, pipe->pipe_0x4400, 0x4400); PIPE_SAVE(dev, pipe->pipe_0x0200, 0x0200); @@ -432,9 +431,9 @@ static void nv10_graph_save_pipe(struct nouveau_channel *chan) static void nv10_graph_load_pipe(struct nouveau_channel *chan) { - struct drm_device *dev = chan->dev; - struct graph_state *pgraph_ctx = chan->pgraph_ctx; + struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR]; struct pipe_state *pipe = &pgraph_ctx->pipe_state; + struct drm_device *dev = chan->dev; uint32_t xfmode0, xfmode1; int i; @@ -482,9 +481,9 @@ static void nv10_graph_load_pipe(struct nouveau_channel *chan) static void nv10_graph_create_pipe(struct nouveau_channel *chan) { - struct drm_device *dev = chan->dev; - struct graph_state *pgraph_ctx = chan->pgraph_ctx; + struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR]; struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state; + struct drm_device *dev = chan->dev; uint32_t *fifo_pipe_state_addr; int i; #define PIPE_INIT(addr) \ @@ -661,8 +660,6 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan, uint32_t inst) { struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; uint32_t st2, st2_dl, st2_dh, fifo_ptr, fifo[0x60/4]; uint32_t ctx_user, ctx_switch[5]; int i, subchan = -1; @@ -711,8 +708,8 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan, 0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c); nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst); nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000); - pgraph->fifo_access(dev, true); - pgraph->fifo_access(dev, false); + nv04_graph_fifo_access(dev, true); + nv04_graph_fifo_access(dev, false); /* Restore the FIFO state */ for (i = 0; i < ARRAY_SIZE(fifo); i++) @@ -729,11 +726,12 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan, nv_wr32(dev, NV10_PGRAPH_CTX_USER, ctx_user); } -int nv10_graph_load_context(struct nouveau_channel *chan) +static int +nv10_graph_load_context(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct graph_state *pgraph_ctx = chan->pgraph_ctx; + struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR]; uint32_t tmp; int i; @@ -757,21 +755,20 @@ int nv10_graph_load_context(struct nouveau_channel *chan) return 0; } -int +static int nv10_graph_unload_context(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_channel *chan; struct graph_state *ctx; uint32_t tmp; int i; - chan = pgraph->channel(dev); + chan = nv10_graph_channel(dev); if (!chan) return 0; - ctx = chan->pgraph_ctx; + ctx = chan->engctx[NVOBJ_ENGINE_GR]; for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) ctx->nv10[i] = nv_rd32(dev, nv10_graph_ctx_regs[i]); @@ -805,7 +802,7 @@ nv10_graph_context_switch(struct drm_device *dev) /* Load context for next channel */ chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f; chan = dev_priv->channels.ptr[chid]; - if (chan && chan->pgraph_ctx) + if (chan && chan->engctx[NVOBJ_ENGINE_GR]) nv10_graph_load_context(chan); } @@ -836,7 +833,8 @@ nv10_graph_channel(struct drm_device *dev) return dev_priv->channels.ptr[chid]; } -int nv10_graph_create_context(struct nouveau_channel *chan) +static int +nv10_graph_context_new(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -844,11 +842,10 @@ int nv10_graph_create_context(struct nouveau_channel *chan) NV_DEBUG(dev, "nv10_graph_context_create %d\n", chan->id); - chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), - GFP_KERNEL); + pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), GFP_KERNEL); if (pgraph_ctx == NULL) return -ENOMEM; - + chan->engctx[engine] = pgraph_ctx; NV_WRITE_CTX(0x00400e88, 0x08000000); NV_WRITE_CTX(0x00400e9c, 0x4b7fffff); @@ -873,30 +870,30 @@ int nv10_graph_create_context(struct nouveau_channel *chan) return 0; } -void nv10_graph_destroy_context(struct nouveau_channel *chan) +static void +nv10_graph_context_del(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct graph_state *pgraph_ctx = chan->pgraph_ctx; + struct graph_state *pgraph_ctx = chan->engctx[engine]; unsigned long flags; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - pgraph->fifo_access(dev, false); + nv04_graph_fifo_access(dev, false); /* Unload the context if it's the currently active one */ - if (pgraph->channel(dev) == chan) - pgraph->unload_context(dev); + if (nv10_graph_channel(dev) == chan) + nv10_graph_unload_context(dev); + + nv04_graph_fifo_access(dev, true); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); /* Free the context resources */ + chan->engctx[engine] = NULL; kfree(pgraph_ctx); - chan->pgraph_ctx = NULL; - - pgraph->fifo_access(dev, true); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); } -void +static void nv10_graph_set_tile_region(struct drm_device *dev, int i) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -907,22 +904,18 @@ nv10_graph_set_tile_region(struct drm_device *dev, int i) nv_wr32(dev, NV10_PGRAPH_TILE(i), tile->addr); } -int nv10_graph_init(struct drm_device *dev) +static int +nv10_graph_init(struct drm_device *dev, int engine) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t tmp; - int ret, i; + u32 tmp; + int i; nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH); nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); - ret = nv10_graph_register(dev); - if (ret) - return ret; - - nouveau_irq_register(dev, 12, nv10_graph_isr); nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); @@ -963,18 +956,20 @@ int nv10_graph_init(struct drm_device *dev) return 0; } -void nv10_graph_takedown(struct drm_device *dev) +static int +nv10_graph_fini(struct drm_device *dev, int engine) { + nv10_graph_unload_context(dev); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000); - nouveau_irq_unregister(dev, 12); + return 0; } static int nv17_graph_mthd_lma_window(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) { + struct graph_state *ctx = chan->engctx[NVOBJ_ENGINE_GR]; struct drm_device *dev = chan->dev; - struct graph_state *ctx = chan->pgraph_ctx; struct pipe_state *pipe = &ctx->pipe_state; uint32_t pipe_0x0040[1], pipe_0x64c0[8], pipe_0x6a80[3], pipe_0x6ab0[3]; uint32_t xfmode0, xfmode1; @@ -1061,64 +1056,13 @@ nv17_graph_mthd_lma_enable(struct nouveau_channel *chan, return 0; } -static int -nv10_graph_register(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (dev_priv->engine.graph.registered) - return 0; - - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ - NVOBJ_CLASS(dev, 0x0030, GR); /* null */ - NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ - NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ - NVOBJ_CLASS(dev, 0x005f, GR); /* imageblit */ - NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */ - NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */ - NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */ - NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */ - NVOBJ_CLASS(dev, 0x0043, GR); /* rop */ - NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */ - NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */ - NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */ - NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */ - NVOBJ_CLASS(dev, 0x0052, GR); /* swzsurf */ - NVOBJ_CLASS(dev, 0x0093, GR); /* surf3d */ - NVOBJ_CLASS(dev, 0x0094, GR); /* tex_tri */ - NVOBJ_CLASS(dev, 0x0095, GR); /* multitex_tri */ - - /* celcius */ - if (dev_priv->chipset <= 0x10) { - NVOBJ_CLASS(dev, 0x0056, GR); - } else - if (dev_priv->chipset < 0x17 || dev_priv->chipset == 0x1a) { - NVOBJ_CLASS(dev, 0x0096, GR); - } else { - NVOBJ_CLASS(dev, 0x0099, GR); - NVOBJ_MTHD (dev, 0x0099, 0x1638, nv17_graph_mthd_lma_window); - NVOBJ_MTHD (dev, 0x0099, 0x163c, nv17_graph_mthd_lma_window); - NVOBJ_MTHD (dev, 0x0099, 0x1640, nv17_graph_mthd_lma_window); - NVOBJ_MTHD (dev, 0x0099, 0x1644, nv17_graph_mthd_lma_window); - NVOBJ_MTHD (dev, 0x0099, 0x1658, nv17_graph_mthd_lma_enable); - } - - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); - - dev_priv->engine.graph.registered = true; - return 0; -} - struct nouveau_bitfield nv10_graph_intr[] = { { NV_PGRAPH_INTR_NOTIFY, "NOTIFY" }, { NV_PGRAPH_INTR_ERROR, "ERROR" }, {} }; -struct nouveau_bitfield nv10_graph_nstatus[] = -{ +struct nouveau_bitfield nv10_graph_nstatus[] = { { NV10_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" }, { NV10_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" }, { NV10_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" }, @@ -1173,3 +1117,73 @@ nv10_graph_isr(struct drm_device *dev) } } } + +static void +nv10_graph_destroy(struct drm_device *dev, int engine) +{ + struct nv10_graph_engine *pgraph = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 12); + kfree(pgraph); +} + +int +nv10_graph_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv10_graph_engine *pgraph; + + pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL); + if (!pgraph) + return -ENOMEM; + + pgraph->base.destroy = nv10_graph_destroy; + pgraph->base.init = nv10_graph_init; + pgraph->base.fini = nv10_graph_fini; + pgraph->base.context_new = nv10_graph_context_new; + pgraph->base.context_del = nv10_graph_context_del; + pgraph->base.object_new = nv04_graph_object_new; + pgraph->base.set_tile_region = nv10_graph_set_tile_region; + + NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); + nouveau_irq_register(dev, 12, nv10_graph_isr); + + /* nvsw */ + NVOBJ_CLASS(dev, 0x506e, SW); + NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); + + NVOBJ_CLASS(dev, 0x0030, GR); /* null */ + NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ + NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ + NVOBJ_CLASS(dev, 0x005f, GR); /* imageblit */ + NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */ + NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */ + NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */ + NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */ + NVOBJ_CLASS(dev, 0x0043, GR); /* rop */ + NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */ + NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */ + NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */ + NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */ + NVOBJ_CLASS(dev, 0x0052, GR); /* swzsurf */ + NVOBJ_CLASS(dev, 0x0093, GR); /* surf3d */ + NVOBJ_CLASS(dev, 0x0094, GR); /* tex_tri */ + NVOBJ_CLASS(dev, 0x0095, GR); /* multitex_tri */ + + /* celcius */ + if (dev_priv->chipset <= 0x10) { + NVOBJ_CLASS(dev, 0x0056, GR); + } else + if (dev_priv->chipset < 0x17 || dev_priv->chipset == 0x1a) { + NVOBJ_CLASS(dev, 0x0096, GR); + } else { + NVOBJ_CLASS(dev, 0x0099, GR); + NVOBJ_MTHD (dev, 0x0099, 0x1638, nv17_graph_mthd_lma_window); + NVOBJ_MTHD (dev, 0x0099, 0x163c, nv17_graph_mthd_lma_window); + NVOBJ_MTHD (dev, 0x0099, 0x1640, nv17_graph_mthd_lma_window); + NVOBJ_MTHD (dev, 0x0099, 0x1644, nv17_graph_mthd_lma_window); + NVOBJ_MTHD (dev, 0x0099, 0x1658, nv17_graph_mthd_lma_enable); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c index 8464b76798d5..affc7d7dd029 100644 --- a/drivers/gpu/drm/nouveau/nv20_graph.c +++ b/drivers/gpu/drm/nouveau/nv20_graph.c @@ -24,6 +24,14 @@ * */ +struct nv20_graph_engine { + struct nouveau_exec_engine base; + struct nouveau_gpuobj *ctxtab; + void (*grctx_init)(struct nouveau_gpuobj *); + u32 grctx_size; + u32 grctx_user; +}; + #define NV20_GRCTX_SIZE (3580*4) #define NV25_GRCTX_SIZE (3529*4) #define NV2A_GRCTX_SIZE (3500*4) @@ -32,12 +40,54 @@ #define NV34_GRCTX_SIZE (18140) #define NV35_36_GRCTX_SIZE (22396) -static int nv20_graph_register(struct drm_device *); -static int nv30_graph_register(struct drm_device *); -static void nv20_graph_isr(struct drm_device *); +int +nv20_graph_unload_context(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; + struct nouveau_channel *chan; + struct nouveau_gpuobj *grctx; + u32 tmp; + + chan = nv10_graph_channel(dev); + if (!chan) + return 0; + grctx = chan->engctx[NVOBJ_ENGINE_GR]; + + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, grctx->pinst >> 4); + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, + NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE); + + nouveau_wait_for_idle(dev); + + nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000); + tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; + tmp |= (pfifo->channels - 1) << 24; + nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); + return 0; +} + +static void +nv20_graph_rdi(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + int i, writecount = 32; + uint32_t rdi_index = 0x2c80000; + + if (dev_priv->chipset == 0x20) { + rdi_index = 0x3d0000; + writecount = 15; + } + + nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index); + for (i = 0; i < writecount; i++) + nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0); + + nouveau_wait_for_idle(dev); +} static void -nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) +nv20_graph_context_init(struct nouveau_gpuobj *ctx) { int i; @@ -87,7 +137,7 @@ nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) } static void -nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) +nv25_graph_context_init(struct nouveau_gpuobj *ctx) { int i; @@ -146,7 +196,7 @@ nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) } static void -nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) +nv2a_graph_context_init(struct nouveau_gpuobj *ctx) { int i; @@ -196,7 +246,7 @@ nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) } static void -nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) +nv30_31_graph_context_init(struct nouveau_gpuobj *ctx) { int i; @@ -254,7 +304,7 @@ nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) } static void -nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) +nv34_graph_context_init(struct nouveau_gpuobj *ctx) { int i; @@ -312,7 +362,7 @@ nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) } static void -nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) +nv35_36_graph_context_init(struct nouveau_gpuobj *ctx) { int i; @@ -370,148 +420,57 @@ nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) } int -nv20_graph_create_context(struct nouveau_channel *chan) +nv20_graph_context_new(struct nouveau_channel *chan, int engine) { + struct nv20_graph_engine *pgraph = nv_engine(chan->dev, engine); + struct nouveau_gpuobj *grctx = NULL; struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *); - unsigned int idoffs = 0x28; int ret; - switch (dev_priv->chipset) { - case 0x20: - ctx_init = nv20_graph_context_init; - idoffs = 0; - break; - case 0x25: - case 0x28: - ctx_init = nv25_graph_context_init; - break; - case 0x2a: - ctx_init = nv2a_graph_context_init; - idoffs = 0; - break; - case 0x30: - case 0x31: - ctx_init = nv30_31_graph_context_init; - break; - case 0x34: - ctx_init = nv34_graph_context_init; - break; - case 0x35: - case 0x36: - ctx_init = nv35_36_graph_context_init; - break; - default: - BUG_ON(1); - } - - ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16, - NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx); + ret = nouveau_gpuobj_new(dev, NULL, pgraph->grctx_size, 16, + NVOBJ_FLAG_ZERO_ALLOC, &grctx); if (ret) return ret; /* Initialise default context values */ - ctx_init(dev, chan->ramin_grctx); + pgraph->grctx_init(grctx); /* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */ - nv_wo32(chan->ramin_grctx, idoffs, - (chan->id << 24) | 0x1); /* CTX_USER */ + /* CTX_USER */ + nv_wo32(grctx, pgraph->grctx_user, (chan->id << 24) | 0x1); - nv_wo32(pgraph->ctx_table, chan->id * 4, chan->ramin_grctx->pinst >> 4); + nv_wo32(pgraph->ctxtab, chan->id * 4, grctx->pinst >> 4); + chan->engctx[engine] = grctx; return 0; } void -nv20_graph_destroy_context(struct nouveau_channel *chan) +nv20_graph_context_del(struct nouveau_channel *chan, int engine) { + struct nv20_graph_engine *pgraph = nv_engine(chan->dev, engine); + struct nouveau_gpuobj *grctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; unsigned long flags; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - pgraph->fifo_access(dev, false); + nv04_graph_fifo_access(dev, false); /* Unload the context if it's the currently active one */ - if (pgraph->channel(dev) == chan) - pgraph->unload_context(dev); + if (nv10_graph_channel(dev) == chan) + nv20_graph_unload_context(dev); - pgraph->fifo_access(dev, true); + nv04_graph_fifo_access(dev, true); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); /* Free the context resources */ - nv_wo32(pgraph->ctx_table, chan->id * 4, 0); - nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); -} - -int -nv20_graph_load_context(struct nouveau_channel *chan) -{ - struct drm_device *dev = chan->dev; - uint32_t inst; + nv_wo32(pgraph->ctxtab, chan->id * 4, 0); - if (!chan->ramin_grctx) - return -EINVAL; - inst = chan->ramin_grctx->pinst >> 4; - - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, - NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD); - nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100); - - nouveau_wait_for_idle(dev); - return 0; -} - -int -nv20_graph_unload_context(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nouveau_channel *chan; - uint32_t inst, tmp; - - chan = pgraph->channel(dev); - if (!chan) - return 0; - inst = chan->ramin_grctx->pinst >> 4; - - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, - NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE); - - nouveau_wait_for_idle(dev); - - nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000); - tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; - tmp |= (pfifo->channels - 1) << 24; - nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); - return 0; + nouveau_gpuobj_ref(NULL, &grctx); + chan->engctx[engine] = NULL; } static void -nv20_graph_rdi(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int i, writecount = 32; - uint32_t rdi_index = 0x2c80000; - - if (dev_priv->chipset == 0x20) { - rdi_index = 0x3d0000; - writecount = 15; - } - - nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index); - for (i = 0; i < writecount; i++) - nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0); - - nouveau_wait_for_idle(dev); -} - -void nv20_graph_set_tile_region(struct drm_device *dev, int i) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -536,56 +495,22 @@ nv20_graph_set_tile_region(struct drm_device *dev, int i) } int -nv20_graph_init(struct drm_device *dev) +nv20_graph_init(struct drm_device *dev, int engine) { + struct nv20_graph_engine *pgraph = nv_engine(dev, engine); struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; uint32_t tmp, vramsz; - int ret, i; - - switch (dev_priv->chipset) { - case 0x20: - pgraph->grctx_size = NV20_GRCTX_SIZE; - break; - case 0x25: - case 0x28: - pgraph->grctx_size = NV25_GRCTX_SIZE; - break; - case 0x2a: - pgraph->grctx_size = NV2A_GRCTX_SIZE; - break; - default: - NV_ERROR(dev, "unknown chipset, disabling acceleration\n"); - pgraph->accel_blocked = true; - return 0; - } + int i; nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH); nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); - if (!pgraph->ctx_table) { - /* Create Context Pointer Table */ - ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16, - NVOBJ_FLAG_ZERO_ALLOC, - &pgraph->ctx_table); - if (ret) - return ret; - } - - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, - pgraph->ctx_table->pinst >> 4); + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, pgraph->ctxtab->pinst >> 4); nv20_graph_rdi(dev); - ret = nv20_graph_register(dev); - if (ret) { - nouveau_gpuobj_ref(NULL, &pgraph->ctx_table); - return ret; - } - - nouveau_irq_register(dev, 12, nv20_graph_isr); nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); @@ -657,67 +582,20 @@ nv20_graph_init(struct drm_device *dev) return 0; } -void -nv20_graph_takedown(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - - nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000); - nouveau_irq_unregister(dev, 12); - - nouveau_gpuobj_ref(NULL, &pgraph->ctx_table); -} - int -nv30_graph_init(struct drm_device *dev) +nv30_graph_init(struct drm_device *dev, int engine) { + struct nv20_graph_engine *pgraph = nv_engine(dev, engine); struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - int ret, i; - - switch (dev_priv->chipset) { - case 0x30: - case 0x31: - pgraph->grctx_size = NV30_31_GRCTX_SIZE; - break; - case 0x34: - pgraph->grctx_size = NV34_GRCTX_SIZE; - break; - case 0x35: - case 0x36: - pgraph->grctx_size = NV35_36_GRCTX_SIZE; - break; - default: - NV_ERROR(dev, "unknown chipset, disabling acceleration\n"); - pgraph->accel_blocked = true; - return 0; - } + int i; nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH); nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); - if (!pgraph->ctx_table) { - /* Create Context Pointer Table */ - ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16, - NVOBJ_FLAG_ZERO_ALLOC, - &pgraph->ctx_table); - if (ret) - return ret; - } - - ret = nv30_graph_register(dev); - if (ret) { - nouveau_gpuobj_ref(NULL, &pgraph->ctx_table); - return ret; - } + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, pgraph->ctxtab->pinst >> 4); - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, - pgraph->ctx_table->pinst >> 4); - - nouveau_irq_register(dev, 12, nv20_graph_isr); nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); @@ -775,85 +653,11 @@ nv30_graph_init(struct drm_device *dev) return 0; } -static int -nv20_graph_register(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (dev_priv->engine.graph.registered) - return 0; - - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ - NVOBJ_CLASS(dev, 0x0030, GR); /* null */ - NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ - NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ - NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */ - NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */ - NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */ - NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */ - NVOBJ_CLASS(dev, 0x0043, GR); /* rop */ - NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */ - NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */ - NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */ - NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */ - NVOBJ_CLASS(dev, 0x009e, GR); /* swzsurf */ - NVOBJ_CLASS(dev, 0x0096, GR); /* celcius */ - - /* kelvin */ - if (dev_priv->chipset < 0x25) - NVOBJ_CLASS(dev, 0x0097, GR); - else - NVOBJ_CLASS(dev, 0x0597, GR); - - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); - - dev_priv->engine.graph.registered = true; - return 0; -} - -static int -nv30_graph_register(struct drm_device *dev) +int +nv20_graph_fini(struct drm_device *dev, int engine) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (dev_priv->engine.graph.registered) - return 0; - - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ - NVOBJ_CLASS(dev, 0x0030, GR); /* null */ - NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ - NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ - NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */ - NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */ - NVOBJ_CLASS(dev, 0x038a, GR); /* ifc (nv30) */ - NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */ - NVOBJ_CLASS(dev, 0x0389, GR); /* sifm (nv30) */ - NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */ - NVOBJ_CLASS(dev, 0x0362, GR); /* surf2d (nv30) */ - NVOBJ_CLASS(dev, 0x0043, GR); /* rop */ - NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */ - NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */ - NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */ - NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */ - NVOBJ_CLASS(dev, 0x039e, GR); /* swzsurf */ - - /* rankine */ - if (0x00000003 & (1 << (dev_priv->chipset & 0x0f))) - NVOBJ_CLASS(dev, 0x0397, GR); - else - if (0x00000010 & (1 << (dev_priv->chipset & 0x0f))) - NVOBJ_CLASS(dev, 0x0697, GR); - else - if (0x000001e0 & (1 << (dev_priv->chipset & 0x0f))) - NVOBJ_CLASS(dev, 0x0497, GR); - - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); - - dev_priv->engine.graph.registered = true; + nv20_graph_unload_context(dev); + nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000); return 0; } @@ -897,3 +701,135 @@ nv20_graph_isr(struct drm_device *dev) } } } + +static void +nv20_graph_destroy(struct drm_device *dev, int engine) +{ + struct nv20_graph_engine *pgraph = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 12); + nouveau_gpuobj_ref(NULL, &pgraph->ctxtab); + + NVOBJ_ENGINE_DEL(dev, GR); + kfree(pgraph); +} + +int +nv20_graph_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv20_graph_engine *pgraph; + int ret; + + pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL); + if (!pgraph) + return -ENOMEM; + + pgraph->base.destroy = nv20_graph_destroy; + pgraph->base.fini = nv20_graph_fini; + pgraph->base.context_new = nv20_graph_context_new; + pgraph->base.context_del = nv20_graph_context_del; + pgraph->base.object_new = nv04_graph_object_new; + pgraph->base.set_tile_region = nv20_graph_set_tile_region; + + pgraph->grctx_user = 0x0028; + if (dev_priv->card_type == NV_20) { + pgraph->base.init = nv20_graph_init; + switch (dev_priv->chipset) { + case 0x20: + pgraph->grctx_init = nv20_graph_context_init; + pgraph->grctx_size = NV20_GRCTX_SIZE; + pgraph->grctx_user = 0x0000; + break; + case 0x25: + case 0x28: + pgraph->grctx_init = nv25_graph_context_init; + pgraph->grctx_size = NV25_GRCTX_SIZE; + break; + case 0x2a: + pgraph->grctx_init = nv2a_graph_context_init; + pgraph->grctx_size = NV2A_GRCTX_SIZE; + pgraph->grctx_user = 0x0000; + break; + default: + NV_ERROR(dev, "PGRAPH: unknown chipset\n"); + return 0; + } + } else { + pgraph->base.init = nv30_graph_init; + switch (dev_priv->chipset) { + case 0x30: + case 0x31: + pgraph->grctx_init = nv30_31_graph_context_init; + pgraph->grctx_size = NV30_31_GRCTX_SIZE; + break; + case 0x34: + pgraph->grctx_init = nv34_graph_context_init; + pgraph->grctx_size = NV34_GRCTX_SIZE; + break; + case 0x35: + case 0x36: + pgraph->grctx_init = nv35_36_graph_context_init; + pgraph->grctx_size = NV35_36_GRCTX_SIZE; + break; + default: + NV_ERROR(dev, "PGRAPH: unknown chipset\n"); + return 0; + } + } + + /* Create Context Pointer Table */ + ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC, + &pgraph->ctxtab); + if (ret) { + kfree(pgraph); + return ret; + } + + NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); + nouveau_irq_register(dev, 12, nv20_graph_isr); + + /* nvsw */ + NVOBJ_CLASS(dev, 0x506e, SW); + NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); + + NVOBJ_CLASS(dev, 0x0030, GR); /* null */ + NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ + NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ + NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */ + NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */ + NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */ + NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */ + NVOBJ_CLASS(dev, 0x0043, GR); /* rop */ + NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */ + NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */ + NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */ + NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */ + if (dev_priv->card_type == NV_20) { + NVOBJ_CLASS(dev, 0x009e, GR); /* swzsurf */ + NVOBJ_CLASS(dev, 0x0096, GR); /* celcius */ + + /* kelvin */ + if (dev_priv->chipset < 0x25) + NVOBJ_CLASS(dev, 0x0097, GR); + else + NVOBJ_CLASS(dev, 0x0597, GR); + } else { + NVOBJ_CLASS(dev, 0x038a, GR); /* ifc (nv30) */ + NVOBJ_CLASS(dev, 0x0389, GR); /* sifm (nv30) */ + NVOBJ_CLASS(dev, 0x0362, GR); /* surf2d (nv30) */ + NVOBJ_CLASS(dev, 0x039e, GR); /* swzsurf */ + + /* rankine */ + if (0x00000003 & (1 << (dev_priv->chipset & 0x0f))) + NVOBJ_CLASS(dev, 0x0397, GR); + else + if (0x00000010 & (1 << (dev_priv->chipset & 0x0f))) + NVOBJ_CLASS(dev, 0x0697, GR); + else + if (0x000001e0 & (1 << (dev_priv->chipset & 0x0f))) + NVOBJ_CLASS(dev, 0x0497, GR); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c index 49b9a35a9cd6..68cb2d991c88 100644 --- a/drivers/gpu/drm/nouveau/nv40_fifo.c +++ b/drivers/gpu/drm/nouveau/nv40_fifo.c @@ -115,6 +115,7 @@ nv40_fifo_do_load_context(struct drm_device *dev, int chid) nv_wr32(dev, 0x32e8, nv_ri32(dev, fc + 68)); nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76)); nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80)); + nv_wr32(dev, 0x330c, nv_ri32(dev, fc + 84)); nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); @@ -186,6 +187,7 @@ nv40_fifo_unload_context(struct drm_device *dev) tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16); nv_wi32(dev, fc + 72, tmp); #endif + nv_wi32(dev, fc + 84, nv_rd32(dev, 0x330c)); nv40_fifo_do_load_context(dev, pfifo->channels - 1); nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c index fceb44c0ec74..5beb01b8ace1 100644 --- a/drivers/gpu/drm/nouveau/nv40_graph.c +++ b/drivers/gpu/drm/nouveau/nv40_graph.c @@ -28,14 +28,18 @@ #include "drm.h" #include "nouveau_drv.h" #include "nouveau_grctx.h" +#include "nouveau_ramht.h" -static int nv40_graph_register(struct drm_device *); -static void nv40_graph_isr(struct drm_device *); +struct nv40_graph_engine { + struct nouveau_exec_engine base; + u32 grctx_size; +}; -struct nouveau_channel * +static struct nouveau_channel * nv40_graph_channel(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *grctx; uint32_t inst; int i; @@ -45,74 +49,17 @@ nv40_graph_channel(struct drm_device *dev) inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4; for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + if (!dev_priv->channels.ptr[i]) + continue; - if (chan && chan->ramin_grctx && - chan->ramin_grctx->pinst == inst) - return chan; + grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR]; + if (grctx && grctx->pinst == inst) + return dev_priv->channels.ptr[i]; } return NULL; } -int -nv40_graph_create_context(struct nouveau_channel *chan) -{ - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nouveau_grctx ctx = {}; - unsigned long flags; - int ret; - - ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16, - NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx); - if (ret) - return ret; - - /* Initialise default context values */ - ctx.dev = chan->dev; - ctx.mode = NOUVEAU_GRCTX_VALS; - ctx.data = chan->ramin_grctx; - nv40_grctx_init(&ctx); - - nv_wo32(chan->ramin_grctx, 0, chan->ramin_grctx->pinst); - - /* init grctx pointer in ramfc, and on PFIFO if channel is - * already active there - */ - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv_wo32(chan->ramfc, 0x38, chan->ramin_grctx->pinst >> 4); - nv_mask(dev, 0x002500, 0x00000001, 0x00000000); - if ((nv_rd32(dev, 0x003204) & 0x0000001f) == chan->id) - nv_wr32(dev, 0x0032e0, chan->ramin_grctx->pinst >> 4); - nv_mask(dev, 0x002500, 0x00000001, 0x00000001); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - return 0; -} - -void -nv40_graph_destroy_context(struct nouveau_channel *chan) -{ - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - unsigned long flags; - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - pgraph->fifo_access(dev, false); - - /* Unload the context if it's the currently active one */ - if (pgraph->channel(dev) == chan) - pgraph->unload_context(dev); - - pgraph->fifo_access(dev, true); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - - /* Free the context resources */ - nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); -} - static int nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save) { @@ -154,57 +101,115 @@ nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save) return 0; } -/* Restore the context for a specific channel into PGRAPH */ -int -nv40_graph_load_context(struct nouveau_channel *chan) +static int +nv40_graph_unload_context(struct drm_device *dev) { - struct drm_device *dev = chan->dev; uint32_t inst; int ret; - if (!chan->ramin_grctx) - return -EINVAL; - inst = chan->ramin_grctx->pinst >> 4; + inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR); + if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED)) + return 0; + inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE; + + ret = nv40_graph_transfer_context(dev, inst, 1); + + nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst); + return ret; +} + +static int +nv40_graph_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv40_graph_engine *pgraph = nv_engine(chan->dev, engine); + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *grctx = NULL; + struct nouveau_grctx ctx = {}; + unsigned long flags; + int ret; - ret = nv40_graph_transfer_context(dev, inst, 0); + ret = nouveau_gpuobj_new(dev, NULL, pgraph->grctx_size, 16, + NVOBJ_FLAG_ZERO_ALLOC, &grctx); if (ret) return ret; - /* 0x40032C, no idea of it's exact function. Could simply be a - * record of the currently active PGRAPH context. It's currently - * unknown as to what bit 24 does. The nv ddx has it set, so we will - * set it here too. - */ - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); - nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, - (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) | - NV40_PGRAPH_CTXCTL_CUR_LOADED); - /* 0x32E0 records the instance address of the active FIFO's PGRAPH - * context. If at any time this doesn't match 0x40032C, you will - * receive PGRAPH_INTR_CONTEXT_SWITCH + /* Initialise default context values */ + ctx.dev = chan->dev; + ctx.mode = NOUVEAU_GRCTX_VALS; + ctx.data = grctx; + nv40_grctx_init(&ctx); + + nv_wo32(grctx, 0, grctx->vinst); + + /* init grctx pointer in ramfc, and on PFIFO if channel is + * already active there */ - nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, inst); + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_wo32(chan->ramfc, 0x38, grctx->vinst >> 4); + nv_mask(dev, 0x002500, 0x00000001, 0x00000000); + if ((nv_rd32(dev, 0x003204) & 0x0000001f) == chan->id) + nv_wr32(dev, 0x0032e0, grctx->vinst >> 4); + nv_mask(dev, 0x002500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + chan->engctx[engine] = grctx; return 0; } -int -nv40_graph_unload_context(struct drm_device *dev) +static void +nv40_graph_context_del(struct nouveau_channel *chan, int engine) { - uint32_t inst; - int ret; + struct nouveau_gpuobj *grctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + unsigned long flags; - inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR); - if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED)) - return 0; - inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE; + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv04_graph_fifo_access(dev, false); - ret = nv40_graph_transfer_context(dev, inst, 1); + /* Unload the context if it's the currently active one */ + if (nv40_graph_channel(dev) == chan) + nv40_graph_unload_context(dev); - nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst); + nv04_graph_fifo_access(dev, true); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + /* Free the context resources */ + nouveau_gpuobj_ref(NULL, &grctx); + chan->engctx[engine] = NULL; +} + +int +nv40_graph_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct drm_device *dev = chan->dev; + struct nouveau_gpuobj *obj = NULL; + int ret; + + ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_FREE, &obj); + if (ret) + return ret; + obj->engine = 1; + obj->class = class; + + nv_wo32(obj, 0x00, class); + nv_wo32(obj, 0x04, 0x00000000); +#ifndef __BIG_ENDIAN + nv_wo32(obj, 0x08, 0x00000000); +#else + nv_wo32(obj, 0x08, 0x01000000); +#endif + nv_wo32(obj, 0x0c, 0x00000000); + nv_wo32(obj, 0x10, 0x00000000); + + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); return ret; } -void +static void nv40_graph_set_tile_region(struct drm_device *dev, int i) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -257,14 +262,14 @@ nv40_graph_set_tile_region(struct drm_device *dev, int i) * C51 0x4e */ int -nv40_graph_init(struct drm_device *dev) +nv40_graph_init(struct drm_device *dev, int engine) { - struct drm_nouveau_private *dev_priv = - (struct drm_nouveau_private *)dev->dev_private; + struct nv40_graph_engine *pgraph = nv_engine(dev, engine); + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; struct nouveau_grctx ctx = {}; uint32_t vramsz, *cp; - int ret, i, j; + int i, j; nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH); @@ -280,7 +285,7 @@ nv40_graph_init(struct drm_device *dev) ctx.data = cp; ctx.ctxprog_max = 256; nv40_grctx_init(&ctx); - dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4; + pgraph->grctx_size = ctx.ctxvals_pos * 4; nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0); for (i = 0; i < ctx.ctxprog_len; i++) @@ -288,14 +293,9 @@ nv40_graph_init(struct drm_device *dev) kfree(cp); - ret = nv40_graph_register(dev); - if (ret) - return ret; - /* No context present currently */ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000); - nouveau_irq_register(dev, 12, nv40_graph_isr); nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF); @@ -428,47 +428,10 @@ nv40_graph_init(struct drm_device *dev) return 0; } -void nv40_graph_takedown(struct drm_device *dev) -{ - nouveau_irq_unregister(dev, 12); -} - static int -nv40_graph_register(struct drm_device *dev) +nv40_graph_fini(struct drm_device *dev, int engine) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (dev_priv->engine.graph.registered) - return 0; - - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ - NVOBJ_CLASS(dev, 0x0030, GR); /* null */ - NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ - NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ - NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */ - NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */ - NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */ - NVOBJ_CLASS(dev, 0x3089, GR); /* sifm (nv40) */ - NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */ - NVOBJ_CLASS(dev, 0x3062, GR); /* surf2d (nv40) */ - NVOBJ_CLASS(dev, 0x0043, GR); /* rop */ - NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */ - NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */ - NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */ - NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */ - NVOBJ_CLASS(dev, 0x309e, GR); /* swzsurf */ - - /* curie */ - if (nv44_graph_class(dev)) - NVOBJ_CLASS(dev, 0x4497, GR); - else - NVOBJ_CLASS(dev, 0x4097, GR); - - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); - - dev_priv->engine.graph.registered = true; + nv40_graph_unload_context(dev); return 0; } @@ -476,17 +439,17 @@ static int nv40_graph_isr_chid(struct drm_device *dev, u32 inst) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_channel *chan; + struct nouveau_gpuobj *grctx; unsigned long flags; int i; spin_lock_irqsave(&dev_priv->channels.lock, flags); for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - chan = dev_priv->channels.ptr[i]; - if (!chan || !chan->ramin_grctx) + if (!dev_priv->channels.ptr[i]) continue; + grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR]; - if (inst == chan->ramin_grctx->pinst) + if (grctx && grctx->pinst == inst) break; } spin_unlock_irqrestore(&dev_priv->channels.lock, flags); @@ -537,3 +500,63 @@ nv40_graph_isr(struct drm_device *dev) } } } + +static void +nv40_graph_destroy(struct drm_device *dev, int engine) +{ + struct nv40_graph_engine *pgraph = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 12); + + NVOBJ_ENGINE_DEL(dev, GR); + kfree(pgraph); +} + +int +nv40_graph_create(struct drm_device *dev) +{ + struct nv40_graph_engine *pgraph; + + pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL); + if (!pgraph) + return -ENOMEM; + + pgraph->base.destroy = nv40_graph_destroy; + pgraph->base.init = nv40_graph_init; + pgraph->base.fini = nv40_graph_fini; + pgraph->base.context_new = nv40_graph_context_new; + pgraph->base.context_del = nv40_graph_context_del; + pgraph->base.object_new = nv40_graph_object_new; + pgraph->base.set_tile_region = nv40_graph_set_tile_region; + + NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); + nouveau_irq_register(dev, 12, nv40_graph_isr); + + NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ + NVOBJ_CLASS(dev, 0x0030, GR); /* null */ + NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ + NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ + NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */ + NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */ + NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */ + NVOBJ_CLASS(dev, 0x3089, GR); /* sifm (nv40) */ + NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */ + NVOBJ_CLASS(dev, 0x3062, GR); /* surf2d (nv40) */ + NVOBJ_CLASS(dev, 0x0043, GR); /* rop */ + NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */ + NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */ + NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */ + NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */ + NVOBJ_CLASS(dev, 0x309e, GR); /* swzsurf */ + + /* curie */ + if (nv44_graph_class(dev)) + NVOBJ_CLASS(dev, 0x4497, GR); + else + NVOBJ_CLASS(dev, 0x4097, GR); + + /* nvsw */ + NVOBJ_CLASS(dev, 0x506e, SW); + NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv40_mpeg.c b/drivers/gpu/drm/nouveau/nv40_mpeg.c new file mode 100644 index 000000000000..6d2af292a2e3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv40_mpeg.c @@ -0,0 +1,311 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_ramht.h" + +struct nv40_mpeg_engine { + struct nouveau_exec_engine base; +}; + +static int +nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *ctx = NULL; + unsigned long flags; + int ret; + + NV_DEBUG(dev, "ch%d\n", chan->id); + + ret = nouveau_gpuobj_new(dev, NULL, 264 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &ctx); + if (ret) + return ret; + + nv_wo32(ctx, 0x78, 0x02001ec1); + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x002500, 0x00000001, 0x00000000); + if ((nv_rd32(dev, 0x003204) & 0x1f) == chan->id) + nv_wr32(dev, 0x00330c, ctx->pinst >> 4); + nv_wo32(chan->ramfc, 0x54, ctx->pinst >> 4); + nv_mask(dev, 0x002500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + chan->engctx[engine] = ctx; + return 0; +} + +static void +nv40_mpeg_context_del(struct nouveau_channel *chan, int engine) +{ + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + struct nouveau_gpuobj *ctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + unsigned long flags; + u32 inst = 0x80000000 | (ctx->pinst >> 4); + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); + if (nv_rd32(dev, 0x00b318) == inst) + nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + nouveau_gpuobj_ref(NULL, &ctx); + chan->engctx[engine] = NULL; +} + +static int +nv40_mpeg_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct drm_device *dev = chan->dev; + struct nouveau_gpuobj *obj = NULL; + int ret; + + ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &obj); + if (ret) + return ret; + obj->engine = 2; + obj->class = class; + + nv_wo32(obj, 0x00, class); + + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; +} + +static int +nv40_mpeg_init(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); + int i; + + /* VPE init */ + nv_mask(dev, 0x000200, 0x00000002, 0x00000000); + nv_mask(dev, 0x000200, 0x00000002, 0x00000002); + nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + + for (i = 0; i < dev_priv->engine.fb.num_tiles; i++) + pmpeg->base.set_tile_region(dev, i); + + /* PMPEG init */ + nv_wr32(dev, 0x00b32c, 0x00000000); + nv_wr32(dev, 0x00b314, 0x00000100); + nv_wr32(dev, 0x00b220, 0x00000044); + nv_wr32(dev, 0x00b300, 0x02001ec1); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); + + nv_wr32(dev, 0x00b100, 0xffffffff); + nv_wr32(dev, 0x00b140, 0xffffffff); + + if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200)); + return -EBUSY; + } + + return 0; +} + +static int +nv40_mpeg_fini(struct drm_device *dev, int engine) +{ + /*XXX: context save? */ + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); + nv_wr32(dev, 0x00b140, 0x00000000); + return 0; +} + +static int +nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + struct drm_device *dev = chan->dev; + u32 inst = data << 4; + u32 dma0 = nv_ri32(dev, inst + 0); + u32 dma1 = nv_ri32(dev, inst + 4); + u32 dma2 = nv_ri32(dev, inst + 8); + u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); + u32 size = dma1 + 1; + + /* only allow linear DMA objects */ + if (!(dma0 & 0x00002000)) + return -EINVAL; + + if (mthd == 0x0190) { + /* DMA_CMD */ + nv_mask(dev, 0x00b300, 0x00030000, (dma0 & 0x00030000)); + nv_wr32(dev, 0x00b334, base); + nv_wr32(dev, 0x00b324, size); + } else + if (mthd == 0x01a0) { + /* DMA_DATA */ + nv_mask(dev, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); + nv_wr32(dev, 0x00b360, base); + nv_wr32(dev, 0x00b364, size); + } else { + /* DMA_IMAGE, VRAM only */ + if (dma0 & 0x000c0000) + return -EINVAL; + + nv_wr32(dev, 0x00b370, base); + nv_wr32(dev, 0x00b374, size); + } + + return 0; +} + +static int +nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *ctx; + unsigned long flags; + int i; + + spin_lock_irqsave(&dev_priv->channels.lock, flags); + for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + if (!dev_priv->channels.ptr[i]) + continue; + + ctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_MPEG]; + if (ctx && ctx->pinst == inst) + break; + } + spin_unlock_irqrestore(&dev_priv->channels.lock, flags); + return i; +} + +static void +nv40_vpe_set_tile_region(struct drm_device *dev, int i) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; + + nv_wr32(dev, 0x00b008 + (i * 0x10), tile->pitch); + nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit); + nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr); +} + +static void +nv40_mpeg_isr(struct drm_device *dev) +{ + u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4; + u32 chid = nv40_mpeg_isr_chid(dev, inst); + u32 stat = nv_rd32(dev, 0x00b100); + u32 type = nv_rd32(dev, 0x00b230); + u32 mthd = nv_rd32(dev, 0x00b234); + u32 data = nv_rd32(dev, 0x00b238); + u32 show = stat; + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nv_mask(dev, 0x00b308, 0x00000000, 0x00000000); + show &= ~0x01000000; + } + + if (type == 0x00000010) { + if (!nouveau_gpuobj_mthd_call2(dev, chid, 0x3174, mthd, data)) + show &= ~0x01000000; + } + } + + nv_wr32(dev, 0x00b100, stat); + nv_wr32(dev, 0x00b230, 0x00000001); + + if (show && nouveau_ratelimit()) { + NV_INFO(dev, "PMPEG: Ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", + chid, inst, stat, type, mthd, data); + } +} + +static void +nv40_vpe_isr(struct drm_device *dev) +{ + if (nv_rd32(dev, 0x00b100)) + nv40_mpeg_isr(dev); + + if (nv_rd32(dev, 0x00b800)) { + u32 stat = nv_rd32(dev, 0x00b800); + NV_INFO(dev, "PMSRCH: 0x%08x\n", stat); + nv_wr32(dev, 0xb800, stat); + } +} + +static void +nv40_mpeg_destroy(struct drm_device *dev, int engine) +{ + struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 0); + + NVOBJ_ENGINE_DEL(dev, MPEG); + kfree(pmpeg); +} + +int +nv40_mpeg_create(struct drm_device *dev) +{ + struct nv40_mpeg_engine *pmpeg; + + pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); + if (!pmpeg) + return -ENOMEM; + + pmpeg->base.destroy = nv40_mpeg_destroy; + pmpeg->base.init = nv40_mpeg_init; + pmpeg->base.fini = nv40_mpeg_fini; + pmpeg->base.context_new = nv40_mpeg_context_new; + pmpeg->base.context_del = nv40_mpeg_context_del; + pmpeg->base.object_new = nv40_mpeg_object_new; + + /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between + * all VPE engines, for this driver's purposes the PMPEG engine + * will be treated as the "master" and handle the global VPE + * bits too + */ + pmpeg->base.set_tile_region = nv40_vpe_set_tile_region; + nouveau_irq_register(dev, 0, nv40_vpe_isr); + + NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); + NVOBJ_CLASS(dev, 0x3174, MPEG); + NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma); + NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma); + NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma); + +#if 0 + NVOBJ_ENGINE_ADD(dev, ME, &pme->base); + NVOBJ_CLASS(dev, 0x4075, ME); +#endif + return 0; + +} diff --git a/drivers/gpu/drm/nouveau/nv50_calc.c b/drivers/gpu/drm/nouveau/nv50_calc.c index de81151648f8..8cf63a8b30cd 100644 --- a/drivers/gpu/drm/nouveau/nv50_calc.c +++ b/drivers/gpu/drm/nouveau/nv50_calc.c @@ -23,7 +23,6 @@ */ #include "drmP.h" -#include "drm_fixed.h" #include "nouveau_drv.h" #include "nouveau_hw.h" @@ -47,45 +46,52 @@ nv50_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk, } int -nv50_calc_pll2(struct drm_device *dev, struct pll_lims *pll, int clk, - int *N, int *fN, int *M, int *P) +nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk, + int *pN, int *pfN, int *pM, int *P) { - fixed20_12 fb_div, a, b; - u32 refclk = pll->refclk / 10; - u32 max_vco_freq = pll->vco1.maxfreq / 10; - u32 max_vco_inputfreq = pll->vco1.max_inputfreq / 10; - clk /= 10; + u32 best_err = ~0, err; + int M, lM, hM, N, fN; - *P = max_vco_freq / clk; + *P = pll->vco1.maxfreq / clk; if (*P > pll->max_p) *P = pll->max_p; if (*P < pll->min_p) *P = pll->min_p; - /* *M = floor((refclk + max_vco_inputfreq) / max_vco_inputfreq); */ - a.full = dfixed_const(refclk + max_vco_inputfreq); - b.full = dfixed_const(max_vco_inputfreq); - a.full = dfixed_div(a, b); - a.full = dfixed_floor(a); - *M = dfixed_trunc(a); + lM = (pll->refclk + pll->vco1.max_inputfreq) / pll->vco1.max_inputfreq; + lM = max(lM, (int)pll->vco1.min_m); + hM = (pll->refclk + pll->vco1.min_inputfreq) / pll->vco1.min_inputfreq; + hM = min(hM, (int)pll->vco1.max_m); - /* fb_div = (vco * *M) / refclk; */ - fb_div.full = dfixed_const(clk * *P); - fb_div.full = dfixed_mul(fb_div, a); - a.full = dfixed_const(refclk); - fb_div.full = dfixed_div(fb_div, a); + for (M = lM; M <= hM; M++) { + u32 tmp = clk * *P * M; + N = tmp / pll->refclk; + fN = tmp % pll->refclk; + if (!pfN && fN >= pll->refclk / 2) + N++; - /* *N = floor(fb_div); */ - a.full = dfixed_floor(fb_div); - *N = dfixed_trunc(fb_div); + if (N < pll->vco1.min_n) + continue; + if (N > pll->vco1.max_n) + break; - /* *fN = (fmod(fb_div, 1.0) * 8192) - 4096; */ - b.full = dfixed_const(8192); - a.full = dfixed_mul(a, b); - fb_div.full = dfixed_mul(fb_div, b); - fb_div.full = fb_div.full - a.full; - *fN = dfixed_trunc(fb_div) - 4096; - *fN &= 0xffff; + err = abs(clk - (pll->refclk * N / M / *P)); + if (err < best_err) { + best_err = err; + *pN = N; + *pM = M; + } - return clk; + if (pfN) { + *pfN = (((fN << 13) / pll->refclk) - 4096) & 0xffff; + return clk; + } + } + + if (unlikely(best_err == ~0)) { + NV_ERROR(dev, "unable to find matching pll values\n"); + return -EINVAL; + } + + return pll->refclk * *pN / *pM / *P; } diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index a19ccaa025b3..ebabacf38da9 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -286,7 +286,7 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) nv_wr32(dev, pll.reg + 8, reg2 | (P << 28) | (M2 << 16) | N2); } else if (dev_priv->chipset < NV_C0) { - ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); + ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P); if (ret <= 0) return 0; @@ -298,7 +298,7 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) nv_wr32(dev, pll.reg + 4, reg1 | (P << 16) | (M1 << 8) | N1); nv_wr32(dev, pll.reg + 8, N2); } else { - ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); + ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P); if (ret <= 0) return 0; @@ -349,14 +349,14 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, struct drm_gem_object *gem; int ret = 0, i; - if (width != 64 || height != 64) - return -EINVAL; - if (!buffer_handle) { nv_crtc->cursor.hide(nv_crtc, true); return 0; } + if (width != 64 || height != 64) + return -EINVAL; + gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); if (!gem) return -ENOENT; @@ -532,8 +532,7 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, if (atomic) { drm_fb = passed_fb; fb = nouveau_framebuffer(passed_fb); - } - else { + } else { /* If not atomic, we can go ahead and pin, and unpin the * old fb we were passed. */ diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 75a376cc342a..74a3f6872701 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -517,13 +517,25 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb, if (bios->fp.if_is_24bit) script |= 0x0200; } else { + /* determine number of lvds links */ + if (nv_connector && nv_connector->edid && + nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { + /* http://www.spwg.org */ + if (((u8 *)nv_connector->edid)[121] == 2) + script |= 0x0100; + } else if (pxclk >= bios->fp.duallink_transition_clk) { script |= 0x0100; + } + + /* determine panel depth */ + if (script & 0x0100) { if (bios->fp.strapless_is_24bit & 2) script |= 0x0200; - } else - if (bios->fp.strapless_is_24bit & 1) - script |= 0x0200; + } else { + if (bios->fp.strapless_is_24bit & 1) + script |= 0x0200; + } if (nv_connector && nv_connector->edid && (nv_connector->edid->revision >= 4) && diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index b02a5b1e7d37..e25cbb46789a 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -31,10 +31,95 @@ #include "nouveau_grctx.h" #include "nouveau_dma.h" #include "nouveau_vm.h" +#include "nouveau_ramht.h" #include "nv50_evo.h" -static int nv50_graph_register(struct drm_device *); -static void nv50_graph_isr(struct drm_device *); +struct nv50_graph_engine { + struct nouveau_exec_engine base; + u32 ctxprog[512]; + u32 ctxprog_size; + u32 grctx_size; +}; + +static void +nv50_graph_fifo_access(struct drm_device *dev, bool enabled) +{ + const uint32_t mask = 0x00010001; + + if (enabled) + nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask); + else + nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask); +} + +static struct nouveau_channel * +nv50_graph_channel(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t inst; + int i; + + /* Be sure we're not in the middle of a context switch or bad things + * will happen, such as unloading the wrong pgraph context. + */ + if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000)) + NV_ERROR(dev, "Ctxprog is still running\n"); + + inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); + if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED)) + return NULL; + inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12; + + for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + + if (chan && chan->ramin && chan->ramin->vinst == inst) + return chan; + } + + return NULL; +} + +static int +nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst) +{ + uint32_t fifo = nv_rd32(dev, 0x400500); + + nv_wr32(dev, 0x400500, fifo & ~1); + nv_wr32(dev, 0x400784, inst); + nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40); + nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11); + nv_wr32(dev, 0x400040, 0xffffffff); + (void)nv_rd32(dev, 0x400040); + nv_wr32(dev, 0x400040, 0x00000000); + nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1); + + if (nouveau_wait_for_idle(dev)) + nv_wr32(dev, 0x40032c, inst | (1<<31)); + nv_wr32(dev, 0x400500, fifo); + + return 0; +} + +static int +nv50_graph_unload_context(struct drm_device *dev) +{ + uint32_t inst; + + inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); + if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED)) + return 0; + inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE; + + nouveau_wait_for_idle(dev); + nv_wr32(dev, 0x400784, inst); + nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20); + nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01); + nouveau_wait_for_idle(dev); + + nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst); + return 0; +} static void nv50_graph_init_reset(struct drm_device *dev) @@ -52,7 +137,6 @@ nv50_graph_init_intr(struct drm_device *dev) { NV_DEBUG(dev, "\n"); - nouveau_irq_register(dev, 12, nv50_graph_isr); nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff); nv_wr32(dev, 0x400138, 0xffffffff); nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff); @@ -135,34 +219,14 @@ nv50_graph_init_zcull(struct drm_device *dev) static int nv50_graph_init_ctxctl(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_grctx ctx = {}; - uint32_t *cp; + struct nv50_graph_engine *pgraph = nv_engine(dev, NVOBJ_ENGINE_GR); int i; NV_DEBUG(dev, "\n"); - cp = kmalloc(512 * 4, GFP_KERNEL); - if (!cp) { - NV_ERROR(dev, "failed to allocate ctxprog\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, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0); + for (i = 0; i < pgraph->ctxprog_size; i++) + nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, pgraph->ctxprog[i]); nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */ nv_wr32(dev, 0x400320, 4); @@ -171,8 +235,8 @@ nv50_graph_init_ctxctl(struct drm_device *dev) return 0; } -int -nv50_graph_init(struct drm_device *dev) +static int +nv50_graph_init(struct drm_device *dev, int engine) { int ret; @@ -186,105 +250,66 @@ nv50_graph_init(struct drm_device *dev) if (ret) return ret; - ret = nv50_graph_register(dev); - if (ret) - return ret; nv50_graph_init_intr(dev); return 0; } -void -nv50_graph_takedown(struct drm_device *dev) +static int +nv50_graph_fini(struct drm_device *dev, int engine) { NV_DEBUG(dev, "\n"); + nv50_graph_unload_context(dev); nv_wr32(dev, 0x40013c, 0x00000000); - nouveau_irq_unregister(dev, 12); -} - -void -nv50_graph_fifo_access(struct drm_device *dev, bool enabled) -{ - const uint32_t mask = 0x00010001; - - if (enabled) - nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask); - else - nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask); -} - -struct nouveau_channel * -nv50_graph_channel(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t inst; - int i; - - /* Be sure we're not in the middle of a context switch or bad things - * will happen, such as unloading the wrong pgraph context. - */ - if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000)) - NV_ERROR(dev, "Ctxprog is still running\n"); - - inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); - if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED)) - return NULL; - inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12; - - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - struct nouveau_channel *chan = dev_priv->channels.ptr[i]; - - if (chan && chan->ramin && chan->ramin->vinst == inst) - return chan; - } - - return NULL; + return 0; } -int -nv50_graph_create_context(struct nouveau_channel *chan) +static int +nv50_graph_context_new(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *ramin = chan->ramin; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; + struct nouveau_gpuobj *grctx = NULL; + struct nv50_graph_engine *pgraph = nv_engine(dev, engine); struct nouveau_grctx ctx = {}; int hdr, ret; NV_DEBUG(dev, "ch%d\n", chan->id); - ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 0, + ret = nouveau_gpuobj_new(dev, NULL, pgraph->grctx_size, 0, NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx); + NVOBJ_FLAG_ZERO_FREE, &grctx); if (ret) return ret; hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20; nv_wo32(ramin, hdr + 0x00, 0x00190002); - nv_wo32(ramin, hdr + 0x04, chan->ramin_grctx->vinst + - pgraph->grctx_size - 1); - nv_wo32(ramin, hdr + 0x08, chan->ramin_grctx->vinst); + nv_wo32(ramin, hdr + 0x04, grctx->vinst + grctx->size - 1); + nv_wo32(ramin, hdr + 0x08, grctx->vinst); nv_wo32(ramin, hdr + 0x0c, 0); nv_wo32(ramin, hdr + 0x10, 0); nv_wo32(ramin, hdr + 0x14, 0x00010000); ctx.dev = chan->dev; ctx.mode = NOUVEAU_GRCTX_VALS; - ctx.data = chan->ramin_grctx; + ctx.data = grctx; nv50_grctx_init(&ctx); - nv_wo32(chan->ramin_grctx, 0x00000, chan->ramin->vinst >> 12); + nv_wo32(grctx, 0x00000, chan->ramin->vinst >> 12); dev_priv->engine.instmem.flush(dev); - atomic_inc(&chan->vm->pgraph_refs); + + atomic_inc(&chan->vm->engref[NVOBJ_ENGINE_GR]); + chan->engctx[NVOBJ_ENGINE_GR] = grctx; return 0; } -void -nv50_graph_destroy_context(struct nouveau_channel *chan) +static void +nv50_graph_context_del(struct nouveau_channel *chan, int engine) { + struct nouveau_gpuobj *grctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; int i, hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20; unsigned long flags; @@ -296,72 +321,49 @@ nv50_graph_destroy_context(struct nouveau_channel *chan) spin_lock_irqsave(&dev_priv->context_switch_lock, flags); pfifo->reassign(dev, false); - pgraph->fifo_access(dev, false); + nv50_graph_fifo_access(dev, false); - if (pgraph->channel(dev) == chan) - pgraph->unload_context(dev); + if (nv50_graph_channel(dev) == chan) + nv50_graph_unload_context(dev); for (i = hdr; i < hdr + 24; i += 4) nv_wo32(chan->ramin, i, 0); dev_priv->engine.instmem.flush(dev); - pgraph->fifo_access(dev, true); + nv50_graph_fifo_access(dev, true); pfifo->reassign(dev, true); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); + nouveau_gpuobj_ref(NULL, &grctx); - atomic_dec(&chan->vm->pgraph_refs); + atomic_dec(&chan->vm->engref[engine]); + chan->engctx[engine] = NULL; } static int -nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst) -{ - uint32_t fifo = nv_rd32(dev, 0x400500); - - nv_wr32(dev, 0x400500, fifo & ~1); - nv_wr32(dev, 0x400784, inst); - nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40); - nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11); - nv_wr32(dev, 0x400040, 0xffffffff); - (void)nv_rd32(dev, 0x400040); - nv_wr32(dev, 0x400040, 0x00000000); - nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1); - - if (nouveau_wait_for_idle(dev)) - nv_wr32(dev, 0x40032c, inst | (1<<31)); - nv_wr32(dev, 0x400500, fifo); - - return 0; -} - -int -nv50_graph_load_context(struct nouveau_channel *chan) -{ - uint32_t inst = chan->ramin->vinst >> 12; - - NV_DEBUG(chan->dev, "ch%d\n", chan->id); - return nv50_graph_do_load_context(chan->dev, inst); -} - -int -nv50_graph_unload_context(struct drm_device *dev) +nv50_graph_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) { - uint32_t inst; + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *obj = NULL; + int ret; - inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); - if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED)) - return 0; - inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE; + ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj); + if (ret) + return ret; + obj->engine = 1; + obj->class = class; - nouveau_wait_for_idle(dev); - nv_wr32(dev, 0x400784, inst); - nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20); - nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01); - nouveau_wait_for_idle(dev); + nv_wo32(obj, 0x00, class); + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + dev_priv->engine.instmem.flush(dev); - nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst); - return 0; + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; } static void @@ -442,68 +444,15 @@ nv50_graph_nvsw_mthd_page_flip(struct nouveau_channel *chan, return 0; } -static int -nv50_graph_register(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (dev_priv->engine.graph.registered) - return 0; - - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ - NVOBJ_MTHD (dev, 0x506e, 0x018c, nv50_graph_nvsw_dma_vblsem); - NVOBJ_MTHD (dev, 0x506e, 0x0400, nv50_graph_nvsw_vblsem_offset); - NVOBJ_MTHD (dev, 0x506e, 0x0404, nv50_graph_nvsw_vblsem_release_val); - NVOBJ_MTHD (dev, 0x506e, 0x0408, nv50_graph_nvsw_vblsem_release); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv50_graph_nvsw_mthd_page_flip); - - NVOBJ_CLASS(dev, 0x0030, GR); /* null */ - NVOBJ_CLASS(dev, 0x5039, GR); /* m2mf */ - NVOBJ_CLASS(dev, 0x502d, GR); /* 2d */ - - /* tesla */ - if (dev_priv->chipset == 0x50) - NVOBJ_CLASS(dev, 0x5097, GR); /* tesla (nv50) */ - else - if (dev_priv->chipset < 0xa0) - NVOBJ_CLASS(dev, 0x8297, GR); /* tesla (nv8x/nv9x) */ - else { - switch (dev_priv->chipset) { - case 0xa0: - case 0xaa: - case 0xac: - NVOBJ_CLASS(dev, 0x8397, GR); - break; - case 0xa3: - case 0xa5: - case 0xa8: - NVOBJ_CLASS(dev, 0x8597, GR); - break; - case 0xaf: - NVOBJ_CLASS(dev, 0x8697, GR); - break; - } - } - - /* compute */ - NVOBJ_CLASS(dev, 0x50c0, GR); - if (dev_priv->chipset > 0xa0 && - dev_priv->chipset != 0xaa && - dev_priv->chipset != 0xac) - NVOBJ_CLASS(dev, 0x85c0, GR); - - dev_priv->engine.graph.registered = true; - return 0; -} -void -nv50_graph_tlb_flush(struct drm_device *dev) +static void +nv50_graph_tlb_flush(struct drm_device *dev, int engine) { nv50_vm_flush_engine(dev, 0); } -void -nv84_graph_tlb_flush(struct drm_device *dev) +static void +nv84_graph_tlb_flush(struct drm_device *dev, int engine) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; @@ -548,8 +497,7 @@ nv84_graph_tlb_flush(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); } -static struct nouveau_enum nv50_mp_exec_error_names[] = -{ +static struct nouveau_enum nv50_mp_exec_error_names[] = { { 3, "STACK_UNDERFLOW", NULL }, { 4, "QUADON_ACTIVE", NULL }, { 8, "TIMEOUT", NULL }, @@ -663,7 +611,7 @@ nv50_pgraph_mp_trap(struct drm_device *dev, int tpid, int display) nv_rd32(dev, addr + 0x20); pc = nv_rd32(dev, addr + 0x24); oplow = nv_rd32(dev, addr + 0x70); - ophigh= nv_rd32(dev, addr + 0x74); + ophigh = nv_rd32(dev, addr + 0x74); NV_INFO(dev, "PGRAPH_TRAP_MP_EXEC - " "TP %d MP %d: ", tpid, i); nouveau_enum_print(nv50_mp_exec_error_names, status); @@ -991,7 +939,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev, u32 display, u64 inst, u32 chid return 1; } -static int +int nv50_graph_isr_chid(struct drm_device *dev, u64 inst) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -1073,3 +1021,101 @@ nv50_graph_isr(struct drm_device *dev) if (nv_rd32(dev, 0x400824) & (1 << 31)) nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31)); } + +static void +nv50_graph_destroy(struct drm_device *dev, int engine) +{ + struct nv50_graph_engine *pgraph = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, GR); + + nouveau_irq_unregister(dev, 12); + kfree(pgraph); +} + +int +nv50_graph_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_graph_engine *pgraph; + struct nouveau_grctx ctx = {}; + int ret; + + pgraph = kzalloc(sizeof(*pgraph),GFP_KERNEL); + if (!pgraph) + return -ENOMEM; + + ctx.dev = dev; + ctx.mode = NOUVEAU_GRCTX_PROG; + ctx.data = pgraph->ctxprog; + ctx.ctxprog_max = ARRAY_SIZE(pgraph->ctxprog); + + ret = nv50_grctx_init(&ctx); + if (ret) { + NV_ERROR(dev, "PGRAPH: ctxprog build failed\n"); + kfree(pgraph); + return 0; + } + + pgraph->grctx_size = ctx.ctxvals_pos * 4; + pgraph->ctxprog_size = ctx.ctxprog_len; + + pgraph->base.destroy = nv50_graph_destroy; + pgraph->base.init = nv50_graph_init; + pgraph->base.fini = nv50_graph_fini; + pgraph->base.context_new = nv50_graph_context_new; + pgraph->base.context_del = nv50_graph_context_del; + pgraph->base.object_new = nv50_graph_object_new; + if (dev_priv->chipset == 0x50 || dev_priv->chipset == 0xac) + pgraph->base.tlb_flush = nv50_graph_tlb_flush; + else + pgraph->base.tlb_flush = nv84_graph_tlb_flush; + + nouveau_irq_register(dev, 12, nv50_graph_isr); + + /* NVSW really doesn't live here... */ + NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ + NVOBJ_MTHD (dev, 0x506e, 0x018c, nv50_graph_nvsw_dma_vblsem); + NVOBJ_MTHD (dev, 0x506e, 0x0400, nv50_graph_nvsw_vblsem_offset); + NVOBJ_MTHD (dev, 0x506e, 0x0404, nv50_graph_nvsw_vblsem_release_val); + NVOBJ_MTHD (dev, 0x506e, 0x0408, nv50_graph_nvsw_vblsem_release); + NVOBJ_MTHD (dev, 0x506e, 0x0500, nv50_graph_nvsw_mthd_page_flip); + + NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); + NVOBJ_CLASS(dev, 0x0030, GR); /* null */ + NVOBJ_CLASS(dev, 0x5039, GR); /* m2mf */ + NVOBJ_CLASS(dev, 0x502d, GR); /* 2d */ + + /* tesla */ + if (dev_priv->chipset == 0x50) + NVOBJ_CLASS(dev, 0x5097, GR); /* tesla (nv50) */ + else + if (dev_priv->chipset < 0xa0) + NVOBJ_CLASS(dev, 0x8297, GR); /* tesla (nv8x/nv9x) */ + else { + switch (dev_priv->chipset) { + case 0xa0: + case 0xaa: + case 0xac: + NVOBJ_CLASS(dev, 0x8397, GR); + break; + case 0xa3: + case 0xa5: + case 0xa8: + NVOBJ_CLASS(dev, 0x8597, GR); + break; + case 0xaf: + NVOBJ_CLASS(dev, 0x8697, GR); + break; + } + } + + /* compute */ + NVOBJ_CLASS(dev, 0x50c0, GR); + if (dev_priv->chipset > 0xa0 && + dev_priv->chipset != 0xaa && + dev_priv->chipset != 0xac) + NVOBJ_CLASS(dev, 0x85c0, GR); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c index 336aab2a24a6..de9abff12b90 100644 --- a/drivers/gpu/drm/nouveau/nv50_grctx.c +++ b/drivers/gpu/drm/nouveau/nv50_grctx.c @@ -747,7 +747,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) gr_def(ctx, offset + 0x64, 0x0000001f); gr_def(ctx, offset + 0x68, 0x0000000f); gr_def(ctx, offset + 0x6c, 0x0000000f); - } else if(dev_priv->chipset < 0xa0) { + } else if (dev_priv->chipset < 0xa0) { cp_ctx(ctx, offset + 0x50, 1); cp_ctx(ctx, offset + 0x70, 1); } else { @@ -924,7 +924,7 @@ nv50_graph_construct_mmio_ddata(struct nouveau_grctx *ctx) dd_emit(ctx, 1, 0); /* 0000007f MULTISAMPLE_SAMPLES_LOG2 */ } else { dd_emit(ctx, 1, 0); /* 0000000f MULTISAMPLE_SAMPLES_LOG2 */ - } + } dd_emit(ctx, 1, 0xc); /* 000000ff SEMANTIC_COLOR.BFC0_ID */ if (dev_priv->chipset != 0x50) dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_COLOR.CLMP_EN */ @@ -1803,9 +1803,7 @@ nv50_graph_construct_gene_unk24xx(struct nouveau_grctx *ctx) xf_emit(ctx, 1, 0); /* 1ff */ xf_emit(ctx, 8, 0); /* 0? */ xf_emit(ctx, 9, 0); /* ffffffff, 7ff */ - } - else - { + } else { xf_emit(ctx, 0xc, 0); /* RO */ /* SEEK */ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ @@ -2836,7 +2834,7 @@ nv50_graph_construct_xfer_tprop(struct nouveau_grctx *ctx) xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ if (IS_NVA3F(dev_priv->chipset)) xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ - if(dev_priv->chipset == 0x50) + if (dev_priv->chipset == 0x50) xf_emit(ctx, 1, 0); /* ff */ else xf_emit(ctx, 3, 0); /* 1, 7, 3ff */ diff --git a/drivers/gpu/drm/nouveau/nv50_mpeg.c b/drivers/gpu/drm/nouveau/nv50_mpeg.c new file mode 100644 index 000000000000..1dc5913f78c5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_mpeg.c @@ -0,0 +1,256 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_ramht.h" + +struct nv50_mpeg_engine { + struct nouveau_exec_engine base; +}; + +static inline u32 +CTX_PTR(struct drm_device *dev, u32 offset) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + if (dev_priv->chipset == 0x50) + offset += 0x0260; + else + offset += 0x0060; + + return offset; +} + +static int +nv50_mpeg_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *ramin = chan->ramin; + struct nouveau_gpuobj *ctx = NULL; + int ret; + + NV_DEBUG(dev, "ch%d\n", chan->id); + + ret = nouveau_gpuobj_new(dev, chan, 128 * 4, 0, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &ctx); + if (ret) + return ret; + + nv_wo32(ramin, CTX_PTR(dev, 0x00), 0x80190002); + nv_wo32(ramin, CTX_PTR(dev, 0x04), ctx->vinst + ctx->size - 1); + nv_wo32(ramin, CTX_PTR(dev, 0x08), ctx->vinst); + nv_wo32(ramin, CTX_PTR(dev, 0x0c), 0); + nv_wo32(ramin, CTX_PTR(dev, 0x10), 0); + nv_wo32(ramin, CTX_PTR(dev, 0x14), 0x00010000); + + nv_wo32(ctx, 0x70, 0x00801ec1); + nv_wo32(ctx, 0x7c, 0x0000037c); + dev_priv->engine.instmem.flush(dev); + + chan->engctx[engine] = ctx; + return 0; +} + +static void +nv50_mpeg_context_del(struct nouveau_channel *chan, int engine) +{ + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + struct nouveau_gpuobj *ctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + unsigned long flags; + u32 inst, i; + + if (!chan->ramin) + return; + + inst = chan->ramin->vinst >> 12; + inst |= 0x80000000; + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); + if (nv_rd32(dev, 0x00b318) == inst) + nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + for (i = 0x00; i <= 0x14; i += 4) + nv_wo32(chan->ramin, CTX_PTR(dev, i), 0x00000000); + nouveau_gpuobj_ref(NULL, &ctx); + chan->engctx[engine] = NULL; +} + +static int +nv50_mpeg_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *obj = NULL; + int ret; + + ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj); + if (ret) + return ret; + obj->engine = 2; + obj->class = class; + + nv_wo32(obj, 0x00, class); + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + dev_priv->engine.instmem.flush(dev); + + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; +} + +static void +nv50_mpeg_tlb_flush(struct drm_device *dev, int engine) +{ + nv50_vm_flush_engine(dev, 0x08); +} + +static int +nv50_mpeg_init(struct drm_device *dev, int engine) +{ + nv_wr32(dev, 0x00b32c, 0x00000000); + nv_wr32(dev, 0x00b314, 0x00000100); + nv_wr32(dev, 0x00b0e0, 0x0000001a); + + nv_wr32(dev, 0x00b220, 0x00000044); + nv_wr32(dev, 0x00b300, 0x00801ec1); + nv_wr32(dev, 0x00b390, 0x00000000); + nv_wr32(dev, 0x00b394, 0x00000000); + nv_wr32(dev, 0x00b398, 0x00000000); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); + + nv_wr32(dev, 0x00b100, 0xffffffff); + nv_wr32(dev, 0x00b140, 0xffffffff); + + if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200)); + return -EBUSY; + } + + return 0; +} + +static int +nv50_mpeg_fini(struct drm_device *dev, int engine) +{ + /*XXX: context save for s/r */ + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); + nv_wr32(dev, 0x00b140, 0x00000000); + return 0; +} + +static void +nv50_mpeg_isr(struct drm_device *dev) +{ + u32 stat = nv_rd32(dev, 0x00b100); + u32 type = nv_rd32(dev, 0x00b230); + u32 mthd = nv_rd32(dev, 0x00b234); + u32 data = nv_rd32(dev, 0x00b238); + u32 show = stat; + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nv_wr32(dev, 0x00b308, 0x00000100); + show &= ~0x01000000; + } + } + + if (show && nouveau_ratelimit()) { + NV_INFO(dev, "PMPEG - 0x%08x 0x%08x 0x%08x 0x%08x\n", + stat, type, mthd, data); + } + + nv_wr32(dev, 0x00b100, stat); + nv_wr32(dev, 0x00b230, 0x00000001); + nv50_fb_vm_trap(dev, 1); +} + +static void +nv50_vpe_isr(struct drm_device *dev) +{ + if (nv_rd32(dev, 0x00b100)) + nv50_mpeg_isr(dev); + + if (nv_rd32(dev, 0x00b800)) { + u32 stat = nv_rd32(dev, 0x00b800); + NV_INFO(dev, "PMSRCH: 0x%08x\n", stat); + nv_wr32(dev, 0xb800, stat); + } +} + +static void +nv50_mpeg_destroy(struct drm_device *dev, int engine) +{ + struct nv50_mpeg_engine *pmpeg = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 0); + + NVOBJ_ENGINE_DEL(dev, MPEG); + kfree(pmpeg); +} + +int +nv50_mpeg_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_mpeg_engine *pmpeg; + + pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); + if (!pmpeg) + return -ENOMEM; + + pmpeg->base.destroy = nv50_mpeg_destroy; + pmpeg->base.init = nv50_mpeg_init; + pmpeg->base.fini = nv50_mpeg_fini; + pmpeg->base.context_new = nv50_mpeg_context_new; + pmpeg->base.context_del = nv50_mpeg_context_del; + pmpeg->base.object_new = nv50_mpeg_object_new; + pmpeg->base.tlb_flush = nv50_mpeg_tlb_flush; + + if (dev_priv->chipset == 0x50) { + nouveau_irq_register(dev, 0, nv50_vpe_isr); + NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); + NVOBJ_CLASS(dev, 0x3174, MPEG); +#if 0 + NVOBJ_ENGINE_ADD(dev, ME, &pme->base); + NVOBJ_CLASS(dev, 0x4075, ME); +#endif + } else { + nouveau_irq_register(dev, 0, nv50_mpeg_isr); + NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); + NVOBJ_CLASS(dev, 0x8274, MPEG); + } + + return 0; + +} diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 7dbb305d7e63..8a2810011bda 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -47,6 +47,21 @@ nv50_pm_clock_get(struct drm_device *dev, u32 id) reg0 = nv_rd32(dev, pll.reg + 0); reg1 = nv_rd32(dev, pll.reg + 4); + + if ((reg0 & 0x80000000) == 0) { + if (id == PLL_SHADER) { + NV_DEBUG(dev, "Shader PLL is disabled. " + "Shader clock is twice the core\n"); + ret = nv50_pm_clock_get(dev, PLL_CORE); + if (ret > 0) + return ret << 1; + } else if (id == PLL_MEMORY) { + NV_DEBUG(dev, "Memory PLL is disabled. " + "Memory clock is equal to the ref_clk\n"); + return pll.refclk; + } + } + P = (reg0 & 0x00070000) >> 16; N = (reg1 & 0x0000ff00) >> 8; M = (reg1 & 0x000000ff); diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c index 6c2694490741..1a0dd491a0e4 100644 --- a/drivers/gpu/drm/nouveau/nv50_vm.c +++ b/drivers/gpu/drm/nouveau/nv50_vm.c @@ -151,8 +151,7 @@ nv50_vm_flush(struct nouveau_vm *vm) struct drm_nouveau_private *dev_priv = vm->dev->dev_private; struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt; + int i; pinstmem->flush(vm->dev); @@ -163,11 +162,10 @@ nv50_vm_flush(struct nouveau_vm *vm) } pfifo->tlb_flush(vm->dev); - - if (atomic_read(&vm->pgraph_refs)) - pgraph->tlb_flush(vm->dev); - if (atomic_read(&vm->pcrypt_refs)) - pcrypt->tlb_flush(vm->dev); + for (i = 0; i < NVOBJ_ENGINE_NR; i++) { + if (atomic_read(&vm->engref[i])) + dev_priv->eng[i]->tlb_flush(vm->dev, i); + } } void diff --git a/drivers/gpu/drm/nouveau/nv84_crypt.c b/drivers/gpu/drm/nouveau/nv84_crypt.c index fabc7fd30b1d..75b809a51748 100644 --- a/drivers/gpu/drm/nouveau/nv84_crypt.c +++ b/drivers/gpu/drm/nouveau/nv84_crypt.c @@ -26,46 +26,48 @@ #include "nouveau_drv.h" #include "nouveau_util.h" #include "nouveau_vm.h" +#include "nouveau_ramht.h" -static void nv84_crypt_isr(struct drm_device *); +struct nv84_crypt_engine { + struct nouveau_exec_engine base; +}; -int -nv84_crypt_create_context(struct nouveau_channel *chan) +static int +nv84_crypt_context_new(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *ramin = chan->ramin; + struct nouveau_gpuobj *ctx; int ret; NV_DEBUG(dev, "ch%d\n", chan->id); - ret = nouveau_gpuobj_new(dev, chan, 256, 0, - NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE, - &chan->crypt_ctx); + ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &ctx); if (ret) return ret; nv_wo32(ramin, 0xa0, 0x00190000); - nv_wo32(ramin, 0xa4, chan->crypt_ctx->vinst + 0xff); - nv_wo32(ramin, 0xa8, chan->crypt_ctx->vinst); + nv_wo32(ramin, 0xa4, ctx->vinst + ctx->size - 1); + nv_wo32(ramin, 0xa8, ctx->vinst); nv_wo32(ramin, 0xac, 0); nv_wo32(ramin, 0xb0, 0); nv_wo32(ramin, 0xb4, 0); - dev_priv->engine.instmem.flush(dev); - atomic_inc(&chan->vm->pcrypt_refs); + + atomic_inc(&chan->vm->engref[engine]); + chan->engctx[engine] = ctx; return 0; } -void -nv84_crypt_destroy_context(struct nouveau_channel *chan) +static void +nv84_crypt_context_del(struct nouveau_channel *chan, int engine) { + struct nouveau_gpuobj *ctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; u32 inst; - if (!chan->crypt_ctx) - return; - inst = (chan->ramin->vinst >> 12); inst |= 0x80000000; @@ -80,43 +82,39 @@ nv84_crypt_destroy_context(struct nouveau_channel *chan) nv_mask(dev, 0x10218c, 0x80000000, 0x00000000); nv_wr32(dev, 0x10200c, 0x00000010); - nouveau_gpuobj_ref(NULL, &chan->crypt_ctx); - atomic_dec(&chan->vm->pcrypt_refs); -} + nouveau_gpuobj_ref(NULL, &ctx); -void -nv84_crypt_tlb_flush(struct drm_device *dev) -{ - nv50_vm_flush_engine(dev, 0x0a); + atomic_dec(&chan->vm->engref[engine]); + chan->engctx[engine] = NULL; } -int -nv84_crypt_init(struct drm_device *dev) +static int +nv84_crypt_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) { + struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt; - - if (!pcrypt->registered) { - NVOBJ_CLASS(dev, 0x74c1, CRYPT); - pcrypt->registered = true; - } + struct nouveau_gpuobj *obj = NULL; + int ret; - nv_mask(dev, 0x000200, 0x00004000, 0x00000000); - nv_mask(dev, 0x000200, 0x00004000, 0x00004000); + ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj); + if (ret) + return ret; + obj->engine = 5; + obj->class = class; - nouveau_irq_register(dev, 14, nv84_crypt_isr); - nv_wr32(dev, 0x102130, 0xffffffff); - nv_wr32(dev, 0x102140, 0xffffffbf); + nv_wo32(obj, 0x00, class); + dev_priv->engine.instmem.flush(dev); - nv_wr32(dev, 0x10200c, 0x00000010); - return 0; + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; } -void -nv84_crypt_fini(struct drm_device *dev) +static void +nv84_crypt_tlb_flush(struct drm_device *dev, int engine) { - nv_wr32(dev, 0x102140, 0x00000000); - nouveau_irq_unregister(dev, 14); + nv50_vm_flush_engine(dev, 0x0a); } static void @@ -138,3 +136,58 @@ nv84_crypt_isr(struct drm_device *dev) nv50_fb_vm_trap(dev, show); } + +static int +nv84_crypt_fini(struct drm_device *dev, int engine) +{ + nv_wr32(dev, 0x102140, 0x00000000); + return 0; +} + +static int +nv84_crypt_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00004000, 0x00000000); + nv_mask(dev, 0x000200, 0x00004000, 0x00004000); + + nv_wr32(dev, 0x102130, 0xffffffff); + nv_wr32(dev, 0x102140, 0xffffffbf); + + nv_wr32(dev, 0x10200c, 0x00000010); + return 0; +} + +static void +nv84_crypt_destroy(struct drm_device *dev, int engine) +{ + struct nv84_crypt_engine *pcrypt = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, CRYPT); + + nouveau_irq_unregister(dev, 14); + kfree(pcrypt); +} + +int +nv84_crypt_create(struct drm_device *dev) +{ + struct nv84_crypt_engine *pcrypt; + + pcrypt = kzalloc(sizeof(*pcrypt), GFP_KERNEL); + if (!pcrypt) + return -ENOMEM; + + pcrypt->base.destroy = nv84_crypt_destroy; + pcrypt->base.init = nv84_crypt_init; + pcrypt->base.fini = nv84_crypt_fini; + pcrypt->base.context_new = nv84_crypt_context_new; + pcrypt->base.context_del = nv84_crypt_context_del; + pcrypt->base.object_new = nv84_crypt_object_new; + pcrypt->base.tlb_flush = nv84_crypt_tlb_flush; + + nouveau_irq_register(dev, 14, nv84_crypt_isr); + + NVOBJ_ENGINE_ADD(dev, CRYPT, &pcrypt->base); + NVOBJ_CLASS (dev, 0x74c1, CRYPT); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nva3_copy.c b/drivers/gpu/drm/nouveau/nva3_copy.c new file mode 100644 index 000000000000..b86820a61220 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nva3_copy.c @@ -0,0 +1,226 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <linux/firmware.h> +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" +#include "nva3_copy.fuc.h" + +struct nva3_copy_engine { + struct nouveau_exec_engine base; +}; + +static int +nva3_copy_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *ramin = chan->ramin; + struct nouveau_gpuobj *ctx = NULL; + int ret; + + NV_DEBUG(dev, "ch%d\n", chan->id); + + ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &ctx); + if (ret) + return ret; + + nv_wo32(ramin, 0xc0, 0x00190000); + nv_wo32(ramin, 0xc4, ctx->vinst + ctx->size - 1); + nv_wo32(ramin, 0xc8, ctx->vinst); + nv_wo32(ramin, 0xcc, 0x00000000); + nv_wo32(ramin, 0xd0, 0x00000000); + nv_wo32(ramin, 0xd4, 0x00000000); + dev_priv->engine.instmem.flush(dev); + + atomic_inc(&chan->vm->engref[engine]); + chan->engctx[engine] = ctx; + return 0; +} + +static int +nva3_copy_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct nouveau_gpuobj *ctx = chan->engctx[engine]; + + /* fuc engine doesn't need an object, our ramht code does.. */ + ctx->engine = 3; + ctx->class = class; + return nouveau_ramht_insert(chan, handle, ctx); +} + +static void +nva3_copy_context_del(struct nouveau_channel *chan, int engine) +{ + struct nouveau_gpuobj *ctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + u32 inst; + + inst = (chan->ramin->vinst >> 12); + inst |= 0x40000000; + + /* disable fifo access */ + nv_wr32(dev, 0x104048, 0x00000000); + /* mark channel as unloaded if it's currently active */ + if (nv_rd32(dev, 0x104050) == inst) + nv_mask(dev, 0x104050, 0x40000000, 0x00000000); + /* mark next channel as invalid if it's about to be loaded */ + if (nv_rd32(dev, 0x104054) == inst) + nv_mask(dev, 0x104054, 0x40000000, 0x00000000); + /* restore fifo access */ + nv_wr32(dev, 0x104048, 0x00000003); + + for (inst = 0xc0; inst <= 0xd4; inst += 4) + nv_wo32(chan->ramin, inst, 0x00000000); + + nouveau_gpuobj_ref(NULL, &ctx); + + atomic_dec(&chan->vm->engref[engine]); + chan->engctx[engine] = ctx; +} + +static void +nva3_copy_tlb_flush(struct drm_device *dev, int engine) +{ + nv50_vm_flush_engine(dev, 0x0d); +} + +static int +nva3_copy_init(struct drm_device *dev, int engine) +{ + int i; + + nv_mask(dev, 0x000200, 0x00002000, 0x00000000); + nv_mask(dev, 0x000200, 0x00002000, 0x00002000); + nv_wr32(dev, 0x104014, 0xffffffff); /* disable all interrupts */ + + /* upload ucode */ + nv_wr32(dev, 0x1041c0, 0x01000000); + for (i = 0; i < sizeof(nva3_pcopy_data) / 4; i++) + nv_wr32(dev, 0x1041c4, nva3_pcopy_data[i]); + + nv_wr32(dev, 0x104180, 0x01000000); + for (i = 0; i < sizeof(nva3_pcopy_code) / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(dev, 0x104188, i >> 6); + nv_wr32(dev, 0x104184, nva3_pcopy_code[i]); + } + + /* start it running */ + nv_wr32(dev, 0x10410c, 0x00000000); + nv_wr32(dev, 0x104104, 0x00000000); /* ENTRY */ + nv_wr32(dev, 0x104100, 0x00000002); /* TRIGGER */ + return 0; +} + +static int +nva3_copy_fini(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x104048, 0x00000003, 0x00000000); + + /* trigger fuc context unload */ + nv_wait(dev, 0x104008, 0x0000000c, 0x00000000); + nv_mask(dev, 0x104054, 0x40000000, 0x00000000); + nv_wr32(dev, 0x104000, 0x00000008); + nv_wait(dev, 0x104008, 0x00000008, 0x00000000); + + nv_wr32(dev, 0x104014, 0xffffffff); + return 0; +} + +static struct nouveau_enum nva3_copy_isr_error_name[] = { + { 0x0001, "ILLEGAL_MTHD" }, + { 0x0002, "INVALID_ENUM" }, + { 0x0003, "INVALID_BITFIELD" }, + {} +}; + +static void +nva3_copy_isr(struct drm_device *dev) +{ + u32 dispatch = nv_rd32(dev, 0x10401c); + u32 stat = nv_rd32(dev, 0x104008) & dispatch & ~(dispatch >> 16); + u32 inst = nv_rd32(dev, 0x104050) & 0x3fffffff; + u32 ssta = nv_rd32(dev, 0x104040) & 0x0000ffff; + u32 addr = nv_rd32(dev, 0x104040) >> 16; + u32 mthd = (addr & 0x07ff) << 2; + u32 subc = (addr & 0x3800) >> 11; + u32 data = nv_rd32(dev, 0x104044); + int chid = nv50_graph_isr_chid(dev, inst); + + if (stat & 0x00000040) { + NV_INFO(dev, "PCOPY: DISPATCH_ERROR ["); + nouveau_enum_print(nva3_copy_isr_error_name, ssta); + printk("] ch %d [0x%08x] subc %d mthd 0x%04x data 0x%08x\n", + chid, inst, subc, mthd, data); + nv_wr32(dev, 0x104004, 0x00000040); + stat &= ~0x00000040; + } + + if (stat) { + NV_INFO(dev, "PCOPY: unhandled intr 0x%08x\n", stat); + nv_wr32(dev, 0x104004, stat); + } + nv50_fb_vm_trap(dev, 1); +} + +static void +nva3_copy_destroy(struct drm_device *dev, int engine) +{ + struct nva3_copy_engine *pcopy = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 22); + + NVOBJ_ENGINE_DEL(dev, COPY0); + kfree(pcopy); +} + +int +nva3_copy_create(struct drm_device *dev) +{ + struct nva3_copy_engine *pcopy; + + pcopy = kzalloc(sizeof(*pcopy), GFP_KERNEL); + if (!pcopy) + return -ENOMEM; + + pcopy->base.destroy = nva3_copy_destroy; + pcopy->base.init = nva3_copy_init; + pcopy->base.fini = nva3_copy_fini; + pcopy->base.context_new = nva3_copy_context_new; + pcopy->base.context_del = nva3_copy_context_del; + pcopy->base.object_new = nva3_copy_object_new; + pcopy->base.tlb_flush = nva3_copy_tlb_flush; + + nouveau_irq_register(dev, 22, nva3_copy_isr); + + NVOBJ_ENGINE_ADD(dev, COPY0, &pcopy->base); + NVOBJ_CLASS(dev, 0x85b5, COPY0); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc b/drivers/gpu/drm/nouveau/nva3_copy.fuc new file mode 100644 index 000000000000..eaf35f8321ee --- /dev/null +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc @@ -0,0 +1,870 @@ +/* fuc microcode for copy engine on nva3- chipsets + * + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +/* To build for nva3:nvc0 + * m4 -DNVA3 nva3_copy.fuc | envyas -a -w -m fuc -V nva3 -o nva3_copy.fuc.h + * + * To build for nvc0- + * m4 -DNVC0 nva3_copy.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_copy.fuc.h + */ + +ifdef(`NVA3', +.section nva3_pcopy_data, +.section nvc0_pcopy_data +) + +ctx_object: .b32 0 +ifdef(`NVA3', +ctx_dma: +ctx_dma_query: .b32 0 +ctx_dma_src: .b32 0 +ctx_dma_dst: .b32 0 +,) +.equ ctx_dma_count 3 +ctx_query_address_high: .b32 0 +ctx_query_address_low: .b32 0 +ctx_query_counter: .b32 0 +ctx_src_address_high: .b32 0 +ctx_src_address_low: .b32 0 +ctx_src_pitch: .b32 0 +ctx_src_tile_mode: .b32 0 +ctx_src_xsize: .b32 0 +ctx_src_ysize: .b32 0 +ctx_src_zsize: .b32 0 +ctx_src_zoff: .b32 0 +ctx_src_xoff: .b32 0 +ctx_src_yoff: .b32 0 +ctx_src_cpp: .b32 0 +ctx_dst_address_high: .b32 0 +ctx_dst_address_low: .b32 0 +ctx_dst_pitch: .b32 0 +ctx_dst_tile_mode: .b32 0 +ctx_dst_xsize: .b32 0 +ctx_dst_ysize: .b32 0 +ctx_dst_zsize: .b32 0 +ctx_dst_zoff: .b32 0 +ctx_dst_xoff: .b32 0 +ctx_dst_yoff: .b32 0 +ctx_dst_cpp: .b32 0 +ctx_format: .b32 0 +ctx_swz_const0: .b32 0 +ctx_swz_const1: .b32 0 +ctx_xcnt: .b32 0 +ctx_ycnt: .b32 0 +.align 256 + +dispatch_table: +// mthd 0x0000, NAME +.b16 0x000 1 +.b32 ctx_object ~0xffffffff +// mthd 0x0100, NOP +.b16 0x040 1 +.b32 0x00010000 + cmd_nop ~0xffffffff +// mthd 0x0140, PM_TRIGGER +.b16 0x050 1 +.b32 0x00010000 + cmd_pm_trigger ~0xffffffff +ifdef(`NVA3', ` +// mthd 0x0180-0x018c, DMA_ +.b16 0x060 ctx_dma_count +dispatch_dma: +.b32 0x00010000 + cmd_dma ~0xffffffff +.b32 0x00010000 + cmd_dma ~0xffffffff +.b32 0x00010000 + cmd_dma ~0xffffffff +',) +// mthd 0x0200-0x0218, SRC_TILE +.b16 0x80 7 +.b32 ctx_src_tile_mode ~0x00000fff +.b32 ctx_src_xsize ~0x0007ffff +.b32 ctx_src_ysize ~0x00001fff +.b32 ctx_src_zsize ~0x000007ff +.b32 ctx_src_zoff ~0x00000fff +.b32 ctx_src_xoff ~0x0007ffff +.b32 ctx_src_yoff ~0x00001fff +// mthd 0x0220-0x0238, DST_TILE +.b16 0x88 7 +.b32 ctx_dst_tile_mode ~0x00000fff +.b32 ctx_dst_xsize ~0x0007ffff +.b32 ctx_dst_ysize ~0x00001fff +.b32 ctx_dst_zsize ~0x000007ff +.b32 ctx_dst_zoff ~0x00000fff +.b32 ctx_dst_xoff ~0x0007ffff +.b32 ctx_dst_yoff ~0x00001fff +// mthd 0x0300-0x0304, EXEC, WRCACHE_FLUSH +.b16 0xc0 2 +.b32 0x00010000 + cmd_exec ~0xffffffff +.b32 0x00010000 + cmd_wrcache_flush ~0xffffffff +// mthd 0x030c-0x0340, various stuff +.b16 0xc3 14 +.b32 ctx_src_address_high ~0x000000ff +.b32 ctx_src_address_low ~0xfffffff0 +.b32 ctx_dst_address_high ~0x000000ff +.b32 ctx_dst_address_low ~0xfffffff0 +.b32 ctx_src_pitch ~0x0007ffff +.b32 ctx_dst_pitch ~0x0007ffff +.b32 ctx_xcnt ~0x0000ffff +.b32 ctx_ycnt ~0x00001fff +.b32 ctx_format ~0x0333ffff +.b32 ctx_swz_const0 ~0xffffffff +.b32 ctx_swz_const1 ~0xffffffff +.b32 ctx_query_address_high ~0x000000ff +.b32 ctx_query_address_low ~0xffffffff +.b32 ctx_query_counter ~0xffffffff +.b16 0x800 0 + +ifdef(`NVA3', +.section nva3_pcopy_code, +.section nvc0_pcopy_code +) + +main: + clear b32 $r0 + mov $sp $r0 + + // setup i0 handler and route fifo and ctxswitch to it + mov $r1 ih + mov $iv0 $r1 + mov $r1 0x400 + movw $r2 0xfff3 + sethi $r2 0 + iowr I[$r2 + 0x300] $r2 + + // enable interrupts + or $r2 0xc + iowr I[$r1] $r2 + bset $flags ie0 + + // enable fifo access and context switching + mov $r1 0x1200 + mov $r2 3 + iowr I[$r1] $r2 + + // sleep forever, waking for interrupts + bset $flags $p0 + spin: + sleep $p0 + bra spin + +// i0 handler +ih: + iord $r1 I[$r0 + 0x200] + + and $r2 $r1 0x00000008 + bra e ih_no_chsw + call chsw + ih_no_chsw: + and $r2 $r1 0x00000004 + bra e ih_no_cmd + call dispatch + + ih_no_cmd: + and $r1 $r1 0x0000000c + iowr I[$r0 + 0x100] $r1 + iret + +// $p1 direction (0 = unload, 1 = load) +// $r3 channel +swctx: + mov $r4 0x7700 + mov $xtargets $r4 +ifdef(`NVA3', ` + // target 7 hardcoded to ctx dma object + mov $xdbase $r0 +', ` // NVC0 + // read SCRATCH3 to decide if we are PCOPY0 or PCOPY1 + mov $r4 0x2100 + iord $r4 I[$r4 + 0] + and $r4 1 + shl b32 $r4 4 + add b32 $r4 0x30 + + // channel is in vram + mov $r15 0x61c + shl b32 $r15 6 + mov $r5 0x114 + iowrs I[$r15] $r5 + + // read 16-byte PCOPYn info, containing context pointer, from channel + shl b32 $r5 $r3 4 + add b32 $r5 2 + mov $xdbase $r5 + mov $r5 $sp + // get a chunk of stack space, aligned to 256 byte boundary + sub b32 $r5 0x100 + mov $r6 0xff + not b32 $r6 + and $r5 $r6 + sethi $r5 0x00020000 + xdld $r4 $r5 + xdwait + sethi $r5 0 + + // set context pointer, from within channel VM + mov $r14 0 + iowrs I[$r15] $r14 + ld b32 $r4 D[$r5 + 0] + shr b32 $r4 8 + ld b32 $r6 D[$r5 + 4] + shl b32 $r6 24 + or $r4 $r6 + mov $xdbase $r4 +') + // 256-byte context, at start of data segment + mov b32 $r4 $r0 + sethi $r4 0x60000 + + // swap! + bra $p1 swctx_load + xdst $r0 $r4 + bra swctx_done + swctx_load: + xdld $r0 $r4 + swctx_done: + xdwait + ret + +chsw: + // read current channel + mov $r2 0x1400 + iord $r3 I[$r2] + + // if it's active, unload it and return + xbit $r15 $r3 0x1e + bra e chsw_no_unload + bclr $flags $p1 + call swctx + bclr $r3 0x1e + iowr I[$r2] $r3 + mov $r4 1 + iowr I[$r2 + 0x200] $r4 + ret + + // read next channel + chsw_no_unload: + iord $r3 I[$r2 + 0x100] + + // is there a channel waiting to be loaded? + xbit $r13 $r3 0x1e + bra e chsw_finish_load + bset $flags $p1 + call swctx +ifdef(`NVA3', + // load dma objects back into TARGET regs + mov $r5 ctx_dma + mov $r6 ctx_dma_count + chsw_load_ctx_dma: + ld b32 $r7 D[$r5 + $r6 * 4] + add b32 $r8 $r6 0x180 + shl b32 $r8 8 + iowr I[$r8] $r7 + sub b32 $r6 1 + bra nc chsw_load_ctx_dma +,) + + chsw_finish_load: + mov $r3 2 + iowr I[$r2 + 0x200] $r3 + ret + +dispatch: + // read incoming fifo command + mov $r3 0x1900 + iord $r2 I[$r3 + 0x100] + iord $r3 I[$r3 + 0x000] + and $r4 $r2 0x7ff + // $r2 will be used to store exception data + shl b32 $r2 0x10 + + // lookup method in the dispatch table, ILLEGAL_MTHD if not found + mov $r5 dispatch_table + clear b32 $r6 + clear b32 $r7 + dispatch_loop: + ld b16 $r6 D[$r5 + 0] + ld b16 $r7 D[$r5 + 2] + add b32 $r5 4 + cmpu b32 $r4 $r6 + bra c dispatch_illegal_mthd + add b32 $r7 $r6 + cmpu b32 $r4 $r7 + bra c dispatch_valid_mthd + sub b32 $r7 $r6 + shl b32 $r7 3 + add b32 $r5 $r7 + bra dispatch_loop + + // ensure no bits set in reserved fields, INVALID_BITFIELD + dispatch_valid_mthd: + sub b32 $r4 $r6 + shl b32 $r4 3 + add b32 $r4 $r5 + ld b32 $r5 D[$r4 + 4] + and $r5 $r3 + cmpu b32 $r5 0 + bra ne dispatch_invalid_bitfield + + // depending on dispatch flags: execute method, or save data as state + ld b16 $r5 D[$r4 + 0] + ld b16 $r6 D[$r4 + 2] + cmpu b32 $r6 0 + bra ne dispatch_cmd + st b32 D[$r5] $r3 + bra dispatch_done + dispatch_cmd: + bclr $flags $p1 + call $r5 + bra $p1 dispatch_error + bra dispatch_done + + dispatch_invalid_bitfield: + or $r2 2 + dispatch_illegal_mthd: + or $r2 1 + + // store exception data in SCRATCH0/SCRATCH1, signal hostirq + dispatch_error: + mov $r4 0x1000 + iowr I[$r4 + 0x000] $r2 + iowr I[$r4 + 0x100] $r3 + mov $r2 0x40 + iowr I[$r0] $r2 + hostirq_wait: + iord $r2 I[$r0 + 0x200] + and $r2 0x40 + cmpu b32 $r2 0 + bra ne hostirq_wait + + dispatch_done: + mov $r2 0x1d00 + mov $r3 1 + iowr I[$r2] $r3 + ret + +// No-operation +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_nop: + ret + +// PM_TRIGGER +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_pm_trigger: + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x20000 + iowr I[$r2] $r3 + ret + +ifdef(`NVA3', +// SET_DMA_* method handler +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_dma: + sub b32 $r4 dispatch_dma + shr b32 $r4 1 + bset $r3 0x1e + st b32 D[$r4 + ctx_dma] $r3 + add b32 $r4 0x600 + shl b32 $r4 6 + iowr I[$r4] $r3 + ret +,) + +// Calculates the hw swizzle mask and adjusts the surface's xcnt to match +// +cmd_exec_set_format: + // zero out a chunk of the stack to store the swizzle into + add $sp -0x10 + st b32 D[$sp + 0x00] $r0 + st b32 D[$sp + 0x04] $r0 + st b32 D[$sp + 0x08] $r0 + st b32 D[$sp + 0x0c] $r0 + + // extract cpp, src_ncomp and dst_ncomp from FORMAT + ld b32 $r4 D[$r0 + ctx_format] + extr $r5 $r4 16:17 + add b32 $r5 1 + extr $r6 $r4 20:21 + add b32 $r6 1 + extr $r7 $r4 24:25 + add b32 $r7 1 + + // convert FORMAT swizzle mask to hw swizzle mask + bclr $flags $p2 + clear b32 $r8 + clear b32 $r9 + ncomp_loop: + and $r10 $r4 0xf + shr b32 $r4 4 + clear b32 $r11 + bpc_loop: + cmpu b8 $r10 4 + bra nc cmp_c0 + mulu $r12 $r10 $r5 + add b32 $r12 $r11 + bset $flags $p2 + bra bpc_next + cmp_c0: + bra ne cmp_c1 + mov $r12 0x10 + add b32 $r12 $r11 + bra bpc_next + cmp_c1: + cmpu b8 $r10 6 + bra nc cmp_zero + mov $r12 0x14 + add b32 $r12 $r11 + bra bpc_next + cmp_zero: + mov $r12 0x80 + bpc_next: + st b8 D[$sp + $r8] $r12 + add b32 $r8 1 + add b32 $r11 1 + cmpu b32 $r11 $r5 + bra c bpc_loop + add b32 $r9 1 + cmpu b32 $r9 $r7 + bra c ncomp_loop + + // SRC_XCNT = (xcnt * src_cpp), or 0 if no src ref in swz (hw will hang) + mulu $r6 $r5 + st b32 D[$r0 + ctx_src_cpp] $r6 + ld b32 $r8 D[$r0 + ctx_xcnt] + mulu $r6 $r8 + bra $p2 dst_xcnt + clear b32 $r6 + + dst_xcnt: + mulu $r7 $r5 + st b32 D[$r0 + ctx_dst_cpp] $r7 + mulu $r7 $r8 + + mov $r5 0x810 + shl b32 $r5 6 + iowr I[$r5 + 0x000] $r6 + iowr I[$r5 + 0x100] $r7 + add b32 $r5 0x800 + ld b32 $r6 D[$r0 + ctx_dst_cpp] + sub b32 $r6 1 + shl b32 $r6 8 + ld b32 $r7 D[$r0 + ctx_src_cpp] + sub b32 $r7 1 + or $r6 $r7 + iowr I[$r5 + 0x000] $r6 + add b32 $r5 0x100 + ld b32 $r6 D[$sp + 0x00] + iowr I[$r5 + 0x000] $r6 + ld b32 $r6 D[$sp + 0x04] + iowr I[$r5 + 0x100] $r6 + ld b32 $r6 D[$sp + 0x08] + iowr I[$r5 + 0x200] $r6 + ld b32 $r6 D[$sp + 0x0c] + iowr I[$r5 + 0x300] $r6 + add b32 $r5 0x400 + ld b32 $r6 D[$r0 + ctx_swz_const0] + iowr I[$r5 + 0x000] $r6 + ld b32 $r6 D[$r0 + ctx_swz_const1] + iowr I[$r5 + 0x100] $r6 + add $sp 0x10 + ret + +// Setup to handle a tiled surface +// +// Calculates a number of parameters the hardware requires in order +// to correctly handle tiling. +// +// Offset calculation is performed as follows (Tp/Th/Td from TILE_MODE): +// nTx = round_up(w * cpp, 1 << Tp) >> Tp +// nTy = round_up(h, 1 << Th) >> Th +// Txo = (x * cpp) & ((1 << Tp) - 1) +// Tx = (x * cpp) >> Tp +// Tyo = y & ((1 << Th) - 1) +// Ty = y >> Th +// Tzo = z & ((1 << Td) - 1) +// Tz = z >> Td +// +// off = (Tzo << Tp << Th) + (Tyo << Tp) + Txo +// off += ((Tz * nTy * nTx)) + (Ty * nTx) + Tx) << Td << Th << Tp; +// +// Inputs: +// $r4: hw command (0x104800) +// $r5: ctx offset adjustment for src/dst selection +// $p2: set if dst surface +// +cmd_exec_set_surface_tiled: + // translate TILE_MODE into Tp, Th, Td shift values + ld b32 $r7 D[$r5 + ctx_src_tile_mode] + extr $r9 $r7 8:11 + extr $r8 $r7 4:7 +ifdef(`NVA3', + add b32 $r8 2 +, + add b32 $r8 3 +) + extr $r7 $r7 0:3 + cmp b32 $r7 0xe + bra ne xtile64 + mov $r7 4 + bra xtileok + xtile64: + xbit $r7 $flags $p2 + add b32 $r7 17 + bset $r4 $r7 + mov $r7 6 + xtileok: + + // Op = (x * cpp) & ((1 << Tp) - 1) + // Tx = (x * cpp) >> Tp + ld b32 $r10 D[$r5 + ctx_src_xoff] + ld b32 $r11 D[$r5 + ctx_src_cpp] + mulu $r10 $r11 + mov $r11 1 + shl b32 $r11 $r7 + sub b32 $r11 1 + and $r12 $r10 $r11 + shr b32 $r10 $r7 + + // Tyo = y & ((1 << Th) - 1) + // Ty = y >> Th + ld b32 $r13 D[$r5 + ctx_src_yoff] + mov $r14 1 + shl b32 $r14 $r8 + sub b32 $r14 1 + and $r11 $r13 $r14 + shr b32 $r13 $r8 + + // YTILE = ((1 << Th) << 12) | ((1 << Th) - Tyo) + add b32 $r14 1 + shl b32 $r15 $r14 12 + sub b32 $r14 $r11 + or $r15 $r14 + xbit $r6 $flags $p2 + add b32 $r6 0x208 + shl b32 $r6 8 + iowr I[$r6 + 0x000] $r15 + + // Op += Tyo << Tp + shl b32 $r11 $r7 + add b32 $r12 $r11 + + // nTx = ((w * cpp) + ((1 << Tp) - 1) >> Tp) + ld b32 $r15 D[$r5 + ctx_src_xsize] + ld b32 $r11 D[$r5 + ctx_src_cpp] + mulu $r15 $r11 + mov $r11 1 + shl b32 $r11 $r7 + sub b32 $r11 1 + add b32 $r15 $r11 + shr b32 $r15 $r7 + push $r15 + + // nTy = (h + ((1 << Th) - 1)) >> Th + ld b32 $r15 D[$r5 + ctx_src_ysize] + mov $r11 1 + shl b32 $r11 $r8 + sub b32 $r11 1 + add b32 $r15 $r11 + shr b32 $r15 $r8 + push $r15 + + // Tys = Tp + Th + // CFG_YZ_TILE_SIZE = ((1 << Th) >> 2) << Td + add b32 $r7 $r8 + sub b32 $r8 2 + mov $r11 1 + shl b32 $r11 $r8 + shl b32 $r11 $r9 + + // Tzo = z & ((1 << Td) - 1) + // Tz = z >> Td + // Op += Tzo << Tys + // Ts = Tys + Td + ld b32 $r8 D[$r5 + ctx_src_zoff] + mov $r14 1 + shl b32 $r14 $r9 + sub b32 $r14 1 + and $r15 $r8 $r14 + shl b32 $r15 $r7 + add b32 $r12 $r15 + add b32 $r7 $r9 + shr b32 $r8 $r9 + + // Ot = ((Tz * nTy * nTx) + (Ty * nTx) + Tx) << Ts + pop $r15 + pop $r9 + mulu $r13 $r9 + add b32 $r10 $r13 + mulu $r8 $r9 + mulu $r8 $r15 + add b32 $r10 $r8 + shl b32 $r10 $r7 + + // PITCH = (nTx - 1) << Ts + sub b32 $r9 1 + shl b32 $r9 $r7 + iowr I[$r6 + 0x200] $r9 + + // SRC_ADDRESS_LOW = (Ot + Op) & 0xffffffff + // CFG_ADDRESS_HIGH |= ((Ot + Op) >> 32) << 16 + ld b32 $r7 D[$r5 + ctx_src_address_low] + ld b32 $r8 D[$r5 + ctx_src_address_high] + add b32 $r10 $r12 + add b32 $r7 $r10 + adc b32 $r8 0 + shl b32 $r8 16 + or $r8 $r11 + sub b32 $r6 0x600 + iowr I[$r6 + 0x000] $r7 + add b32 $r6 0x400 + iowr I[$r6 + 0x000] $r8 + ret + +// Setup to handle a linear surface +// +// Nothing to see here.. Sets ADDRESS and PITCH, pretty non-exciting +// +cmd_exec_set_surface_linear: + xbit $r6 $flags $p2 + add b32 $r6 0x202 + shl b32 $r6 8 + ld b32 $r7 D[$r5 + ctx_src_address_low] + iowr I[$r6 + 0x000] $r7 + add b32 $r6 0x400 + ld b32 $r7 D[$r5 + ctx_src_address_high] + shl b32 $r7 16 + iowr I[$r6 + 0x000] $r7 + add b32 $r6 0x400 + ld b32 $r7 D[$r5 + ctx_src_pitch] + iowr I[$r6 + 0x000] $r7 + ret + +// wait for regs to be available for use +cmd_exec_wait: + push $r0 + push $r1 + mov $r0 0x800 + shl b32 $r0 6 + loop: + iord $r1 I[$r0] + and $r1 1 + bra ne loop + pop $r1 + pop $r0 + ret + +cmd_exec_query: + // if QUERY_SHORT not set, write out { -, 0, TIME_LO, TIME_HI } + xbit $r4 $r3 13 + bra ne query_counter + call cmd_exec_wait + mov $r4 0x80c + shl b32 $r4 6 + ld b32 $r5 D[$r0 + ctx_query_address_low] + add b32 $r5 4 + iowr I[$r4 + 0x000] $r5 + iowr I[$r4 + 0x100] $r0 + mov $r5 0xc + iowr I[$r4 + 0x200] $r5 + add b32 $r4 0x400 + ld b32 $r5 D[$r0 + ctx_query_address_high] + shl b32 $r5 16 + iowr I[$r4 + 0x000] $r5 + add b32 $r4 0x500 + mov $r5 0x00000b00 + sethi $r5 0x00010000 + iowr I[$r4 + 0x000] $r5 + mov $r5 0x00004040 + shl b32 $r5 1 + sethi $r5 0x80800000 + iowr I[$r4 + 0x100] $r5 + mov $r5 0x00001110 + sethi $r5 0x13120000 + iowr I[$r4 + 0x200] $r5 + mov $r5 0x00001514 + sethi $r5 0x17160000 + iowr I[$r4 + 0x300] $r5 + mov $r5 0x00002601 + sethi $r5 0x00010000 + mov $r4 0x800 + shl b32 $r4 6 + iowr I[$r4 + 0x000] $r5 + + // write COUNTER + query_counter: + call cmd_exec_wait + mov $r4 0x80c + shl b32 $r4 6 + ld b32 $r5 D[$r0 + ctx_query_address_low] + iowr I[$r4 + 0x000] $r5 + iowr I[$r4 + 0x100] $r0 + mov $r5 0x4 + iowr I[$r4 + 0x200] $r5 + add b32 $r4 0x400 + ld b32 $r5 D[$r0 + ctx_query_address_high] + shl b32 $r5 16 + iowr I[$r4 + 0x000] $r5 + add b32 $r4 0x500 + mov $r5 0x00000300 + iowr I[$r4 + 0x000] $r5 + mov $r5 0x00001110 + sethi $r5 0x13120000 + iowr I[$r4 + 0x100] $r5 + ld b32 $r5 D[$r0 + ctx_query_counter] + add b32 $r4 0x500 + iowr I[$r4 + 0x000] $r5 + mov $r5 0x00002601 + sethi $r5 0x00010000 + mov $r4 0x800 + shl b32 $r4 6 + iowr I[$r4 + 0x000] $r5 + ret + +// Execute a copy operation +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// 000002000 QUERY_SHORT +// 000001000 QUERY +// 000000100 DST_LINEAR +// 000000010 SRC_LINEAR +// 000000001 FORMAT +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_exec: + call cmd_exec_wait + + // if format requested, call function to calculate it, otherwise + // fill in cpp/xcnt for both surfaces as if (cpp == 1) + xbit $r15 $r3 0 + bra e cmd_exec_no_format + call cmd_exec_set_format + mov $r4 0x200 + bra cmd_exec_init_src_surface + cmd_exec_no_format: + mov $r6 0x810 + shl b32 $r6 6 + mov $r7 1 + st b32 D[$r0 + ctx_src_cpp] $r7 + st b32 D[$r0 + ctx_dst_cpp] $r7 + ld b32 $r7 D[$r0 + ctx_xcnt] + iowr I[$r6 + 0x000] $r7 + iowr I[$r6 + 0x100] $r7 + clear b32 $r4 + + cmd_exec_init_src_surface: + bclr $flags $p2 + clear b32 $r5 + xbit $r15 $r3 4 + bra e src_tiled + call cmd_exec_set_surface_linear + bra cmd_exec_init_dst_surface + src_tiled: + call cmd_exec_set_surface_tiled + bset $r4 7 + + cmd_exec_init_dst_surface: + bset $flags $p2 + mov $r5 ctx_dst_address_high - ctx_src_address_high + xbit $r15 $r3 8 + bra e dst_tiled + call cmd_exec_set_surface_linear + bra cmd_exec_kick + dst_tiled: + call cmd_exec_set_surface_tiled + bset $r4 8 + + cmd_exec_kick: + mov $r5 0x800 + shl b32 $r5 6 + ld b32 $r6 D[$r0 + ctx_ycnt] + iowr I[$r5 + 0x100] $r6 + mov $r6 0x0041 + // SRC_TARGET = 1, DST_TARGET = 2 + sethi $r6 0x44000000 + or $r4 $r6 + iowr I[$r5] $r4 + + // if requested, queue up a QUERY write after the copy has completed + xbit $r15 $r3 12 + bra e cmd_exec_done + call cmd_exec_query + + cmd_exec_done: + ret + +// Flush write cache +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_wrcache_flush: + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x10000 + iowr I[$r2] $r3 + ret + +.align 0x100 diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h new file mode 100644 index 000000000000..2731de22ebe9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h @@ -0,0 +1,534 @@ +uint32_t nva3_pcopy_data[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00010000, + 0x00000000, + 0x00000000, + 0x00010040, + 0x00010160, + 0x00000000, + 0x00010050, + 0x00010162, + 0x00000000, + 0x00030060, + 0x00010170, + 0x00000000, + 0x00010170, + 0x00000000, + 0x00010170, + 0x00000000, + 0x00070080, + 0x00000028, + 0xfffff000, + 0x0000002c, + 0xfff80000, + 0x00000030, + 0xffffe000, + 0x00000034, + 0xfffff800, + 0x00000038, + 0xfffff000, + 0x0000003c, + 0xfff80000, + 0x00000040, + 0xffffe000, + 0x00070088, + 0x00000054, + 0xfffff000, + 0x00000058, + 0xfff80000, + 0x0000005c, + 0xffffe000, + 0x00000060, + 0xfffff800, + 0x00000064, + 0xfffff000, + 0x00000068, + 0xfff80000, + 0x0000006c, + 0xffffe000, + 0x000200c0, + 0x00010492, + 0x00000000, + 0x0001051b, + 0x00000000, + 0x000e00c3, + 0x0000001c, + 0xffffff00, + 0x00000020, + 0x0000000f, + 0x00000048, + 0xffffff00, + 0x0000004c, + 0x0000000f, + 0x00000024, + 0xfff80000, + 0x00000050, + 0xfff80000, + 0x00000080, + 0xffff0000, + 0x00000084, + 0xffffe000, + 0x00000074, + 0xfccc0000, + 0x00000078, + 0x00000000, + 0x0000007c, + 0x00000000, + 0x00000010, + 0xffffff00, + 0x00000014, + 0x00000000, + 0x00000018, + 0x00000000, + 0x00000800, +}; + +uint32_t nva3_pcopy_code[] = { + 0x04fe04bd, + 0x3517f000, + 0xf10010fe, + 0xf1040017, + 0xf0fff327, + 0x22d00023, + 0x0c25f0c0, + 0xf40012d0, + 0x17f11031, + 0x27f01200, + 0x0012d003, + 0xf40031f4, + 0x0ef40028, + 0x8001cffd, + 0xf40812c4, + 0x21f4060b, + 0x0412c472, + 0xf4060bf4, + 0x11c4c321, + 0x4001d00c, + 0x47f101f8, + 0x4bfe7700, + 0x0007fe00, + 0xf00204b9, + 0x01f40643, + 0x0604fa09, + 0xfa060ef4, + 0x03f80504, + 0x27f100f8, + 0x23cf1400, + 0x1e3fc800, + 0xf4170bf4, + 0x21f40132, + 0x1e3af052, + 0xf00023d0, + 0x24d00147, + 0xcf00f880, + 0x3dc84023, + 0x220bf41e, + 0xf40131f4, + 0x57f05221, + 0x0367f004, + 0xa07856bc, + 0xb6018068, + 0x87d00884, + 0x0162b600, + 0xf0f018f4, + 0x23d00237, + 0xf100f880, + 0xcf190037, + 0x33cf4032, + 0xff24e400, + 0x1024b607, + 0x010057f1, + 0x74bd64bd, + 0x58005658, + 0x50b60157, + 0x0446b804, + 0xbb4d08f4, + 0x47b80076, + 0x0f08f404, + 0xb60276bb, + 0x57bb0374, + 0xdf0ef400, + 0xb60246bb, + 0x45bb0344, + 0x01459800, + 0xb00453fd, + 0x1bf40054, + 0x00455820, + 0xb0014658, + 0x1bf40064, + 0x00538009, + 0xf4300ef4, + 0x55f90132, + 0xf40c01f4, + 0x25f0250e, + 0x0125f002, + 0x100047f1, + 0xd00042d0, + 0x27f04043, + 0x0002d040, + 0xf08002cf, + 0x24b04024, + 0xf71bf400, + 0x1d0027f1, + 0xd00137f0, + 0x00f80023, + 0x27f100f8, + 0x34bd2200, + 0xd00233f0, + 0x00f80023, + 0x012842b7, + 0xf00145b6, + 0x43801e39, + 0x0040b701, + 0x0644b606, + 0xf80043d0, + 0xf030f400, + 0xb00001b0, + 0x01b00101, + 0x0301b002, + 0xc71d0498, + 0x50b63045, + 0x3446c701, + 0xc70160b6, + 0x70b63847, + 0x0232f401, + 0x94bd84bd, + 0xb60f4ac4, + 0xb4bd0445, + 0xf404a430, + 0xa5ff0f18, + 0x00cbbbc0, + 0xf40231f4, + 0x1bf4220e, + 0x10c7f00c, + 0xf400cbbb, + 0xa430160e, + 0x0c18f406, + 0xbb14c7f0, + 0x0ef400cb, + 0x80c7f107, + 0x01c83800, + 0xb60180b6, + 0xb5b801b0, + 0xc308f404, + 0xb80190b6, + 0x08f40497, + 0x0065fdb2, + 0x98110680, + 0x68fd2008, + 0x0502f400, + 0x75fd64bd, + 0x1c078000, + 0xf10078fd, + 0xb6081057, + 0x56d00654, + 0x4057d000, + 0x080050b7, + 0xb61c0698, + 0x64b60162, + 0x11079808, + 0xfd0172b6, + 0x56d00567, + 0x0050b700, + 0x0060b401, + 0xb40056d0, + 0x56d00160, + 0x0260b440, + 0xb48056d0, + 0x56d00360, + 0x0050b7c0, + 0x1e069804, + 0x980056d0, + 0x56d01f06, + 0x1030f440, + 0x579800f8, + 0x6879c70a, + 0xb66478c7, + 0x77c70280, + 0x0e76b060, + 0xf0091bf4, + 0x0ef40477, + 0x027cf00f, + 0xfd1170b6, + 0x77f00947, + 0x0f5a9806, + 0xfd115b98, + 0xb7f000ab, + 0x04b7bb01, + 0xff01b2b6, + 0xa7bbc4ab, + 0x105d9805, + 0xbb01e7f0, + 0xe2b604e8, + 0xb4deff01, + 0xb605d8bb, + 0xef9401e0, + 0x02ebbb0c, + 0xf005fefd, + 0x60b7026c, + 0x64b60208, + 0x006fd008, + 0xbb04b7bb, + 0x5f9800cb, + 0x115b980b, + 0xf000fbfd, + 0xb7bb01b7, + 0x01b2b604, + 0xbb00fbbb, + 0xf0f905f7, + 0xf00c5f98, + 0xb8bb01b7, + 0x01b2b604, + 0xbb00fbbb, + 0xf0f905f8, + 0xb60078bb, + 0xb7f00282, + 0x04b8bb01, + 0x9804b9bb, + 0xe7f00e58, + 0x04e9bb01, + 0xff01e2b6, + 0xf7bbf48e, + 0x00cfbb04, + 0xbb0079bb, + 0xf0fc0589, + 0xd9fd90fc, + 0x00adbb00, + 0xfd0089fd, + 0xa8bb008f, + 0x04a7bb00, + 0xbb0192b6, + 0x69d00497, + 0x08579880, + 0xbb075898, + 0x7abb00ac, + 0x0081b600, + 0xfd1084b6, + 0x62b7058b, + 0x67d00600, + 0x0060b700, + 0x0068d004, + 0x6cf000f8, + 0x0260b702, + 0x0864b602, + 0xd0085798, + 0x60b70067, + 0x57980400, + 0x1074b607, + 0xb70067d0, + 0x98040060, + 0x67d00957, + 0xf900f800, + 0xf110f900, + 0xb6080007, + 0x01cf0604, + 0x0114f000, + 0xfcfa1bf4, + 0xf800fc10, + 0x0d34c800, + 0xf5701bf4, + 0xf103ab21, + 0xb6080c47, + 0x05980644, + 0x0450b605, + 0xd00045d0, + 0x57f04040, + 0x8045d00c, + 0x040040b7, + 0xb6040598, + 0x45d01054, + 0x0040b700, + 0x0057f105, + 0x0153f00b, + 0xf10045d0, + 0xb6404057, + 0x53f10154, + 0x45d08080, + 0x1057f140, + 0x1253f111, + 0x8045d013, + 0x151457f1, + 0x171653f1, + 0xf1c045d0, + 0xf0260157, + 0x47f10153, + 0x44b60800, + 0x0045d006, + 0x03ab21f5, + 0x080c47f1, + 0x980644b6, + 0x45d00505, + 0x4040d000, + 0xd00457f0, + 0x40b78045, + 0x05980400, + 0x1054b604, + 0xb70045d0, + 0xf1050040, + 0xd0030057, + 0x57f10045, + 0x53f11110, + 0x45d01312, + 0x06059840, + 0x050040b7, + 0xf10045d0, + 0xf0260157, + 0x47f10153, + 0x44b60800, + 0x0045d006, + 0x21f500f8, + 0x3fc803ab, + 0x0e0bf400, + 0x018921f5, + 0x020047f1, + 0xf11e0ef4, + 0xb6081067, + 0x77f00664, + 0x11078001, + 0x981c0780, + 0x67d02007, + 0x4067d000, + 0x32f444bd, + 0xc854bd02, + 0x0bf4043f, + 0x8221f50a, + 0x0a0ef403, + 0x027621f5, + 0xf40749f0, + 0x57f00231, + 0x083fc82c, + 0xf50a0bf4, + 0xf4038221, + 0x21f50a0e, + 0x49f00276, + 0x0057f108, + 0x0654b608, + 0xd0210698, + 0x67f04056, + 0x0063f141, + 0x0546fd44, + 0xc80054d0, + 0x0bf40c3f, + 0xc521f507, + 0xf100f803, + 0xbd220027, + 0x0133f034, + 0xf80023d0, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index dbbafed36406..e4b2b9e934b2 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -27,32 +27,74 @@ #include "nouveau_bios.h" #include "nouveau_pm.h" -/*XXX: boards using limits 0x40 need fixing, the register layout - * is correct here, but, there's some other funny magic - * that modifies things, so it's not likely we'll set/read - * the correct timings yet.. working on it... +/* This is actually a lot more complex than it appears here, but hopefully + * this should be able to deal with what the VBIOS leaves for us.. + * + * If not, well, I'll jump off that bridge when I come to it. */ struct nva3_pm_state { - struct pll_lims pll; - int N, M, P; + enum pll_types type; + u32 src0; + u32 src1; + u32 ctrl; + u32 coef; + u32 old_pnm; + u32 new_pnm; + u32 new_div; }; +static int +nva3_pm_pll_offset(u32 id) +{ + static const u32 pll_map[] = { + 0x00, PLL_CORE, + 0x01, PLL_SHADER, + 0x02, PLL_MEMORY, + 0x00, 0x00 + }; + const u32 *map = pll_map; + + while (map[1]) { + if (id == map[1]) + return map[0]; + map += 2; + } + + return -ENOENT; +} + int nva3_pm_clock_get(struct drm_device *dev, u32 id) { + u32 src0, src1, ctrl, coef; struct pll_lims pll; - int P, N, M, ret; - u32 reg; + int ret, off; + int P, N, M; ret = get_pll_limits(dev, id, &pll); if (ret) return ret; - reg = nv_rd32(dev, pll.reg + 4); - P = (reg & 0x003f0000) >> 16; - N = (reg & 0x0000ff00) >> 8; - M = (reg & 0x000000ff); + off = nva3_pm_pll_offset(id); + if (off < 0) + return off; + + src0 = nv_rd32(dev, 0x4120 + (off * 4)); + src1 = nv_rd32(dev, 0x4160 + (off * 4)); + ctrl = nv_rd32(dev, pll.reg + 0); + coef = nv_rd32(dev, pll.reg + 4); + NV_DEBUG(dev, "PLL %02x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + id, src0, src1, ctrl, coef); + + if (ctrl & 0x00000008) { + u32 div = ((src1 & 0x003c0000) >> 18) + 1; + return (pll.refclk * 2) / div; + } + + P = (coef & 0x003f0000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff); return pll.refclk * N / M / P; } @@ -60,36 +102,103 @@ void * nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, u32 id, int khz) { - struct nva3_pm_state *state; - int dummy, ret; + struct nva3_pm_state *pll; + struct pll_lims limits; + int N, M, P, diff; + int ret, off; + + ret = get_pll_limits(dev, id, &limits); + if (ret < 0) + return (ret == -ENOENT) ? NULL : ERR_PTR(ret); + + off = nva3_pm_pll_offset(id); + if (id < 0) + return ERR_PTR(-EINVAL); - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) return ERR_PTR(-ENOMEM); + pll->type = id; + pll->src0 = 0x004120 + (off * 4); + pll->src1 = 0x004160 + (off * 4); + pll->ctrl = limits.reg + 0; + pll->coef = limits.reg + 4; - ret = get_pll_limits(dev, id, &state->pll); - if (ret < 0) { - kfree(state); - return (ret == -ENOENT) ? NULL : ERR_PTR(ret); + /* If target clock is within [-2, 3) MHz of a divisor, we'll + * use that instead of calculating MNP values + */ + pll->new_div = min((limits.refclk * 2) / (khz - 2999), 16); + if (pll->new_div) { + diff = khz - ((limits.refclk * 2) / pll->new_div); + if (diff < -2000 || diff >= 3000) + pll->new_div = 0; } - ret = nv50_calc_pll2(dev, &state->pll, khz, &state->N, &dummy, - &state->M, &state->P); - if (ret < 0) { - kfree(state); - return ERR_PTR(ret); + if (!pll->new_div) { + ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P); + if (ret < 0) + return ERR_PTR(ret); + + pll->new_pnm = (P << 16) | (N << 8) | M; + pll->new_div = 2 - 1; + } else { + pll->new_pnm = 0; + pll->new_div--; } - return state; + if ((nv_rd32(dev, pll->src1) & 0x00000101) != 0x00000101) + pll->old_pnm = nv_rd32(dev, pll->coef); + return pll; } void nva3_pm_clock_set(struct drm_device *dev, void *pre_state) { - struct nva3_pm_state *state = pre_state; - u32 reg = state->pll.reg; + struct nva3_pm_state *pll = pre_state; + u32 ctrl = 0; + + /* For the memory clock, NVIDIA will build a "script" describing + * the reclocking process and ask PDAEMON to execute it. + */ + if (pll->type == PLL_MEMORY) { + nv_wr32(dev, 0x100210, 0); + nv_wr32(dev, 0x1002dc, 1); + nv_wr32(dev, 0x004018, 0x00001000); + ctrl = 0x18000100; + } + + if (pll->old_pnm || !pll->new_pnm) { + nv_mask(dev, pll->src1, 0x003c0101, 0x00000101 | + (pll->new_div << 18)); + nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl); + nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000); + } + + if (pll->new_pnm) { + nv_mask(dev, pll->src0, 0x00000101, 0x00000101); + nv_wr32(dev, pll->coef, pll->new_pnm); + nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl); + nv_mask(dev, pll->ctrl, 0x00000010, 0x00000000); + nv_mask(dev, pll->ctrl, 0x00020010, 0x00020010); + nv_wr32(dev, pll->ctrl, 0x00010015 | ctrl); + nv_mask(dev, pll->src1, 0x00000100, 0x00000000); + nv_mask(dev, pll->src1, 0x00000001, 0x00000000); + if (pll->type == PLL_MEMORY) + nv_wr32(dev, 0x4018, 0x10005000); + } else { + nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000); + nv_mask(dev, pll->src0, 0x00000100, 0x00000000); + nv_mask(dev, pll->src0, 0x00000001, 0x00000000); + if (pll->type == PLL_MEMORY) + nv_wr32(dev, 0x4018, 0x1000d000); + } + + if (pll->type == PLL_MEMORY) { + nv_wr32(dev, 0x1002dc, 0); + nv_wr32(dev, 0x100210, 0x80000000); + } - nv_wr32(dev, reg + 4, (state->P << 16) | (state->N << 8) | state->M); - kfree(state); + kfree(pll); } diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.c b/drivers/gpu/drm/nouveau/nvc0_copy.c new file mode 100644 index 000000000000..208fa7ab3f42 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_copy.c @@ -0,0 +1,243 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <linux/firmware.h> +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" +#include "nvc0_copy.fuc.h" + +struct nvc0_copy_engine { + struct nouveau_exec_engine base; + u32 irq; + u32 pmc; + u32 fuc; + u32 ctx; +}; + +static int +nvc0_copy_context_new(struct nouveau_channel *chan, int engine) +{ + struct nvc0_copy_engine *pcopy = nv_engine(chan->dev, engine); + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *ramin = chan->ramin; + struct nouveau_gpuobj *ctx = NULL; + int ret; + + ret = nouveau_gpuobj_new(dev, NULL, 256, 256, + NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER | + NVOBJ_FLAG_ZERO_ALLOC, &ctx); + if (ret) + return ret; + + nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->vinst)); + nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->vinst)); + dev_priv->engine.instmem.flush(dev); + + chan->engctx[engine] = ctx; + return 0; +} + +static int +nvc0_copy_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + return 0; +} + +static void +nvc0_copy_context_del(struct nouveau_channel *chan, int engine) +{ + struct nvc0_copy_engine *pcopy = nv_engine(chan->dev, engine); + struct nouveau_gpuobj *ctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + u32 inst; + + inst = (chan->ramin->vinst >> 12); + inst |= 0x40000000; + + /* disable fifo access */ + nv_wr32(dev, pcopy->fuc + 0x048, 0x00000000); + /* mark channel as unloaded if it's currently active */ + if (nv_rd32(dev, pcopy->fuc + 0x050) == inst) + nv_mask(dev, pcopy->fuc + 0x050, 0x40000000, 0x00000000); + /* mark next channel as invalid if it's about to be loaded */ + if (nv_rd32(dev, pcopy->fuc + 0x054) == inst) + nv_mask(dev, pcopy->fuc + 0x054, 0x40000000, 0x00000000); + /* restore fifo access */ + nv_wr32(dev, pcopy->fuc + 0x048, 0x00000003); + + nv_wo32(chan->ramin, pcopy->ctx + 0, 0x00000000); + nv_wo32(chan->ramin, pcopy->ctx + 4, 0x00000000); + nouveau_gpuobj_ref(NULL, &ctx); + + chan->engctx[engine] = ctx; +} + +static int +nvc0_copy_init(struct drm_device *dev, int engine) +{ + struct nvc0_copy_engine *pcopy = nv_engine(dev, engine); + int i; + + nv_mask(dev, 0x000200, pcopy->pmc, 0x00000000); + nv_mask(dev, 0x000200, pcopy->pmc, pcopy->pmc); + nv_wr32(dev, pcopy->fuc + 0x014, 0xffffffff); + + nv_wr32(dev, pcopy->fuc + 0x1c0, 0x01000000); + for (i = 0; i < sizeof(nvc0_pcopy_data) / 4; i++) + nv_wr32(dev, pcopy->fuc + 0x1c4, nvc0_pcopy_data[i]); + + nv_wr32(dev, pcopy->fuc + 0x180, 0x01000000); + for (i = 0; i < sizeof(nvc0_pcopy_code) / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(dev, pcopy->fuc + 0x188, i >> 6); + nv_wr32(dev, pcopy->fuc + 0x184, nvc0_pcopy_code[i]); + } + + nv_wr32(dev, pcopy->fuc + 0x084, engine - NVOBJ_ENGINE_COPY0); + nv_wr32(dev, pcopy->fuc + 0x10c, 0x00000000); + nv_wr32(dev, pcopy->fuc + 0x104, 0x00000000); /* ENTRY */ + nv_wr32(dev, pcopy->fuc + 0x100, 0x00000002); /* TRIGGER */ + return 0; +} + +static int +nvc0_copy_fini(struct drm_device *dev, int engine) +{ + struct nvc0_copy_engine *pcopy = nv_engine(dev, engine); + + nv_mask(dev, pcopy->fuc + 0x048, 0x00000003, 0x00000000); + + /* trigger fuc context unload */ + nv_wait(dev, pcopy->fuc + 0x008, 0x0000000c, 0x00000000); + nv_mask(dev, pcopy->fuc + 0x054, 0x40000000, 0x00000000); + nv_wr32(dev, pcopy->fuc + 0x000, 0x00000008); + nv_wait(dev, pcopy->fuc + 0x008, 0x00000008, 0x00000000); + + nv_wr32(dev, pcopy->fuc + 0x014, 0xffffffff); + return 0; +} + +static struct nouveau_enum nvc0_copy_isr_error_name[] = { + { 0x0001, "ILLEGAL_MTHD" }, + { 0x0002, "INVALID_ENUM" }, + { 0x0003, "INVALID_BITFIELD" }, + {} +}; + +static void +nvc0_copy_isr(struct drm_device *dev, int engine) +{ + struct nvc0_copy_engine *pcopy = nv_engine(dev, engine); + u32 disp = nv_rd32(dev, pcopy->fuc + 0x01c); + u32 stat = nv_rd32(dev, pcopy->fuc + 0x008) & disp & ~(disp >> 16); + u64 inst = (u64)(nv_rd32(dev, pcopy->fuc + 0x050) & 0x0fffffff) << 12; + u32 chid = nvc0_graph_isr_chid(dev, inst); + u32 ssta = nv_rd32(dev, pcopy->fuc + 0x040) & 0x0000ffff; + u32 addr = nv_rd32(dev, pcopy->fuc + 0x040) >> 16; + u32 mthd = (addr & 0x07ff) << 2; + u32 subc = (addr & 0x3800) >> 11; + u32 data = nv_rd32(dev, pcopy->fuc + 0x044); + + if (stat & 0x00000040) { + NV_INFO(dev, "PCOPY: DISPATCH_ERROR ["); + nouveau_enum_print(nvc0_copy_isr_error_name, ssta); + printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n", + chid, inst, subc, mthd, data); + nv_wr32(dev, pcopy->fuc + 0x004, 0x00000040); + stat &= ~0x00000040; + } + + if (stat) { + NV_INFO(dev, "PCOPY: unhandled intr 0x%08x\n", stat); + nv_wr32(dev, pcopy->fuc + 0x004, stat); + } +} + +static void +nvc0_copy_isr_0(struct drm_device *dev) +{ + nvc0_copy_isr(dev, NVOBJ_ENGINE_COPY0); +} + +static void +nvc0_copy_isr_1(struct drm_device *dev) +{ + nvc0_copy_isr(dev, NVOBJ_ENGINE_COPY1); +} + +static void +nvc0_copy_destroy(struct drm_device *dev, int engine) +{ + struct nvc0_copy_engine *pcopy = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, pcopy->irq); + + if (engine == NVOBJ_ENGINE_COPY0) + NVOBJ_ENGINE_DEL(dev, COPY0); + else + NVOBJ_ENGINE_DEL(dev, COPY1); + kfree(pcopy); +} + +int +nvc0_copy_create(struct drm_device *dev, int engine) +{ + struct nvc0_copy_engine *pcopy; + + pcopy = kzalloc(sizeof(*pcopy), GFP_KERNEL); + if (!pcopy) + return -ENOMEM; + + pcopy->base.destroy = nvc0_copy_destroy; + pcopy->base.init = nvc0_copy_init; + pcopy->base.fini = nvc0_copy_fini; + pcopy->base.context_new = nvc0_copy_context_new; + pcopy->base.context_del = nvc0_copy_context_del; + pcopy->base.object_new = nvc0_copy_object_new; + + if (engine == 0) { + pcopy->irq = 5; + pcopy->pmc = 0x00000040; + pcopy->fuc = 0x104000; + pcopy->ctx = 0x0230; + nouveau_irq_register(dev, pcopy->irq, nvc0_copy_isr_0); + NVOBJ_ENGINE_ADD(dev, COPY0, &pcopy->base); + NVOBJ_CLASS(dev, 0x90b5, COPY0); + } else { + pcopy->irq = 6; + pcopy->pmc = 0x00000080; + pcopy->fuc = 0x105000; + pcopy->ctx = 0x0240; + nouveau_irq_register(dev, pcopy->irq, nvc0_copy_isr_1); + NVOBJ_ENGINE_ADD(dev, COPY1, &pcopy->base); + NVOBJ_CLASS(dev, 0x90b8, COPY1); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h new file mode 100644 index 000000000000..419903880e9d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h @@ -0,0 +1,527 @@ +uint32_t nvc0_pcopy_data[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00010000, + 0x00000000, + 0x00000000, + 0x00010040, + 0x0001019f, + 0x00000000, + 0x00010050, + 0x000101a1, + 0x00000000, + 0x00070080, + 0x0000001c, + 0xfffff000, + 0x00000020, + 0xfff80000, + 0x00000024, + 0xffffe000, + 0x00000028, + 0xfffff800, + 0x0000002c, + 0xfffff000, + 0x00000030, + 0xfff80000, + 0x00000034, + 0xffffe000, + 0x00070088, + 0x00000048, + 0xfffff000, + 0x0000004c, + 0xfff80000, + 0x00000050, + 0xffffe000, + 0x00000054, + 0xfffff800, + 0x00000058, + 0xfffff000, + 0x0000005c, + 0xfff80000, + 0x00000060, + 0xffffe000, + 0x000200c0, + 0x000104b8, + 0x00000000, + 0x00010541, + 0x00000000, + 0x000e00c3, + 0x00000010, + 0xffffff00, + 0x00000014, + 0x0000000f, + 0x0000003c, + 0xffffff00, + 0x00000040, + 0x0000000f, + 0x00000018, + 0xfff80000, + 0x00000044, + 0xfff80000, + 0x00000074, + 0xffff0000, + 0x00000078, + 0xffffe000, + 0x00000068, + 0xfccc0000, + 0x0000006c, + 0x00000000, + 0x00000070, + 0x00000000, + 0x00000004, + 0xffffff00, + 0x00000008, + 0x00000000, + 0x0000000c, + 0x00000000, + 0x00000800, +}; + +uint32_t nvc0_pcopy_code[] = { + 0x04fe04bd, + 0x3517f000, + 0xf10010fe, + 0xf1040017, + 0xf0fff327, + 0x22d00023, + 0x0c25f0c0, + 0xf40012d0, + 0x17f11031, + 0x27f01200, + 0x0012d003, + 0xf40031f4, + 0x0ef40028, + 0x8001cffd, + 0xf40812c4, + 0x21f4060b, + 0x0412c4ca, + 0xf5070bf4, + 0xc4010221, + 0x01d00c11, + 0xf101f840, + 0xfe770047, + 0x47f1004b, + 0x44cf2100, + 0x0144f000, + 0xb60444b6, + 0xf7f13040, + 0xf4b6061c, + 0x1457f106, + 0x00f5d101, + 0xb6043594, + 0x57fe0250, + 0x0145fe00, + 0x010052b7, + 0x00ff67f1, + 0x56fd60bd, + 0x0253f004, + 0xf80545fa, + 0x0053f003, + 0xd100e7f0, + 0x549800fe, + 0x0845b600, + 0xb6015698, + 0x46fd1864, + 0x0047fe05, + 0xf00204b9, + 0x01f40643, + 0x0604fa09, + 0xfa060ef4, + 0x03f80504, + 0x27f100f8, + 0x23cf1400, + 0x1e3fc800, + 0xf4170bf4, + 0x21f40132, + 0x1e3af053, + 0xf00023d0, + 0x24d00147, + 0xcf00f880, + 0x3dc84023, + 0x090bf41e, + 0xf40131f4, + 0x37f05321, + 0x8023d002, + 0x37f100f8, + 0x32cf1900, + 0x0033cf40, + 0x07ff24e4, + 0xf11024b6, + 0xbd010057, + 0x5874bd64, + 0x57580056, + 0x0450b601, + 0xf40446b8, + 0x76bb4d08, + 0x0447b800, + 0xbb0f08f4, + 0x74b60276, + 0x0057bb03, + 0xbbdf0ef4, + 0x44b60246, + 0x0045bb03, + 0xfd014598, + 0x54b00453, + 0x201bf400, + 0x58004558, + 0x64b00146, + 0x091bf400, + 0xf4005380, + 0x32f4300e, + 0xf455f901, + 0x0ef40c01, + 0x0225f025, + 0xf10125f0, + 0xd0100047, + 0x43d00042, + 0x4027f040, + 0xcf0002d0, + 0x24f08002, + 0x0024b040, + 0xf1f71bf4, + 0xf01d0027, + 0x23d00137, + 0xf800f800, + 0x0027f100, + 0xf034bd22, + 0x23d00233, + 0xf400f800, + 0x01b0f030, + 0x0101b000, + 0xb00201b0, + 0x04980301, + 0x3045c71a, + 0xc70150b6, + 0x60b63446, + 0x3847c701, + 0xf40170b6, + 0x84bd0232, + 0x4ac494bd, + 0x0445b60f, + 0xa430b4bd, + 0x0f18f404, + 0xbbc0a5ff, + 0x31f400cb, + 0x220ef402, + 0xf00c1bf4, + 0xcbbb10c7, + 0x160ef400, + 0xf406a430, + 0xc7f00c18, + 0x00cbbb14, + 0xf1070ef4, + 0x380080c7, + 0x80b601c8, + 0x01b0b601, + 0xf404b5b8, + 0x90b6c308, + 0x0497b801, + 0xfdb208f4, + 0x06800065, + 0x1d08980e, + 0xf40068fd, + 0x64bd0502, + 0x800075fd, + 0x78fd1907, + 0x1057f100, + 0x0654b608, + 0xd00056d0, + 0x50b74057, + 0x06980800, + 0x0162b619, + 0x980864b6, + 0x72b60e07, + 0x0567fd01, + 0xb70056d0, + 0xb4010050, + 0x56d00060, + 0x0160b400, + 0xb44056d0, + 0x56d00260, + 0x0360b480, + 0xb7c056d0, + 0x98040050, + 0x56d01b06, + 0x1c069800, + 0xf44056d0, + 0x00f81030, + 0xc7075798, + 0x78c76879, + 0x0380b664, + 0xb06077c7, + 0x1bf40e76, + 0x0477f009, + 0xf00f0ef4, + 0x70b6027c, + 0x0947fd11, + 0x980677f0, + 0x5b980c5a, + 0x00abfd0e, + 0xbb01b7f0, + 0xb2b604b7, + 0xc4abff01, + 0x9805a7bb, + 0xe7f00d5d, + 0x04e8bb01, + 0xff01e2b6, + 0xd8bbb4de, + 0x01e0b605, + 0xbb0cef94, + 0xfefd02eb, + 0x026cf005, + 0x020860b7, + 0xd00864b6, + 0xb7bb006f, + 0x00cbbb04, + 0x98085f98, + 0xfbfd0e5b, + 0x01b7f000, + 0xb604b7bb, + 0xfbbb01b2, + 0x05f7bb00, + 0x5f98f0f9, + 0x01b7f009, + 0xb604b8bb, + 0xfbbb01b2, + 0x05f8bb00, + 0x78bbf0f9, + 0x0282b600, + 0xbb01b7f0, + 0xb9bb04b8, + 0x0b589804, + 0xbb01e7f0, + 0xe2b604e9, + 0xf48eff01, + 0xbb04f7bb, + 0x79bb00cf, + 0x0589bb00, + 0x90fcf0fc, + 0xbb00d9fd, + 0x89fd00ad, + 0x008ffd00, + 0xbb00a8bb, + 0x92b604a7, + 0x0497bb01, + 0x988069d0, + 0x58980557, + 0x00acbb04, + 0xb6007abb, + 0x84b60081, + 0x058bfd10, + 0x060062b7, + 0xb70067d0, + 0xd0040060, + 0x00f80068, + 0xb7026cf0, + 0xb6020260, + 0x57980864, + 0x0067d005, + 0x040060b7, + 0xb6045798, + 0x67d01074, + 0x0060b700, + 0x06579804, + 0xf80067d0, + 0xf900f900, + 0x0007f110, + 0x0604b608, + 0xf00001cf, + 0x1bf40114, + 0xfc10fcfa, + 0xc800f800, + 0x1bf40d34, + 0xd121f570, + 0x0c47f103, + 0x0644b608, + 0xb6020598, + 0x45d00450, + 0x4040d000, + 0xd00c57f0, + 0x40b78045, + 0x05980400, + 0x1054b601, + 0xb70045d0, + 0xf1050040, + 0xf00b0057, + 0x45d00153, + 0x4057f100, + 0x0154b640, + 0x808053f1, + 0xf14045d0, + 0xf1111057, + 0xd0131253, + 0x57f18045, + 0x53f11514, + 0x45d01716, + 0x0157f1c0, + 0x0153f026, + 0x080047f1, + 0xd00644b6, + 0x21f50045, + 0x47f103d1, + 0x44b6080c, + 0x02059806, + 0xd00045d0, + 0x57f04040, + 0x8045d004, + 0x040040b7, + 0xb6010598, + 0x45d01054, + 0x0040b700, + 0x0057f105, + 0x0045d003, + 0x111057f1, + 0x131253f1, + 0x984045d0, + 0x40b70305, + 0x45d00500, + 0x0157f100, + 0x0153f026, + 0x080047f1, + 0xd00644b6, + 0x00f80045, + 0x03d121f5, + 0xf4003fc8, + 0x21f50e0b, + 0x47f101af, + 0x0ef40200, + 0x1067f11e, + 0x0664b608, + 0x800177f0, + 0x07800e07, + 0x1d079819, + 0xd00067d0, + 0x44bd4067, + 0xbd0232f4, + 0x043fc854, + 0xf50a0bf4, + 0xf403a821, + 0x21f50a0e, + 0x49f0029c, + 0x0231f407, + 0xc82c57f0, + 0x0bf4083f, + 0xa821f50a, + 0x0a0ef403, + 0x029c21f5, + 0xf10849f0, + 0xb6080057, + 0x06980654, + 0x4056d01e, + 0xf14167f0, + 0xfd440063, + 0x54d00546, + 0x0c3fc800, + 0xf5070bf4, + 0xf803eb21, + 0x0027f100, + 0xf034bd22, + 0x23d00133, + 0x0000f800, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c index 2886f2726a9e..fb4f5943e01b 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fifo.c +++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c @@ -37,7 +37,7 @@ struct nvc0_fifo_priv { }; struct nvc0_fifo_chan { - struct nouveau_bo *user; + struct nouveau_gpuobj *user; struct nouveau_gpuobj *ramfc; }; @@ -106,7 +106,7 @@ nvc0_fifo_create_context(struct nouveau_channel *chan) struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nvc0_fifo_priv *priv = pfifo->priv; struct nvc0_fifo_chan *fifoch; - u64 ib_virt, user_vinst; + u64 ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4; int ret; chan->fifo_priv = kzalloc(sizeof(*fifoch), GFP_KERNEL); @@ -115,28 +115,13 @@ nvc0_fifo_create_context(struct nouveau_channel *chan) fifoch = chan->fifo_priv; /* allocate vram for control regs, map into polling area */ - ret = nouveau_bo_new(dev, NULL, 0x1000, 0, TTM_PL_FLAG_VRAM, - 0, 0, &fifoch->user); + ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &fifoch->user); if (ret) goto error; - ret = nouveau_bo_pin(fifoch->user, TTM_PL_FLAG_VRAM); - if (ret) { - nouveau_bo_ref(NULL, &fifoch->user); - goto error; - } - - user_vinst = fifoch->user->bo.mem.start << PAGE_SHIFT; - - ret = nouveau_bo_map(fifoch->user); - if (ret) { - nouveau_bo_unpin(fifoch->user); - nouveau_bo_ref(NULL, &fifoch->user); - goto error; - } - nouveau_vm_map_at(&priv->user_vma, chan->id * 0x1000, - fifoch->user->bo.mem.mm_node); + *(struct nouveau_mem **)fifoch->user->node); chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) + priv->user_vma.offset + (chan->id * 0x1000), @@ -146,20 +131,6 @@ nvc0_fifo_create_context(struct nouveau_channel *chan) goto error; } - ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4; - - /* zero channel regs */ - nouveau_bo_wr32(fifoch->user, 0x0040/4, 0); - nouveau_bo_wr32(fifoch->user, 0x0044/4, 0); - nouveau_bo_wr32(fifoch->user, 0x0048/4, 0); - nouveau_bo_wr32(fifoch->user, 0x004c/4, 0); - nouveau_bo_wr32(fifoch->user, 0x0050/4, 0); - nouveau_bo_wr32(fifoch->user, 0x0058/4, 0); - nouveau_bo_wr32(fifoch->user, 0x005c/4, 0); - nouveau_bo_wr32(fifoch->user, 0x0060/4, 0); - nouveau_bo_wr32(fifoch->user, 0x0088/4, 0); - nouveau_bo_wr32(fifoch->user, 0x008c/4, 0); - /* ramfc */ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst, chan->ramin->vinst, 0x100, @@ -167,8 +138,8 @@ nvc0_fifo_create_context(struct nouveau_channel *chan) if (ret) goto error; - nv_wo32(fifoch->ramfc, 0x08, lower_32_bits(user_vinst)); - nv_wo32(fifoch->ramfc, 0x0c, upper_32_bits(user_vinst)); + nv_wo32(fifoch->ramfc, 0x08, lower_32_bits(fifoch->user->vinst)); + nv_wo32(fifoch->ramfc, 0x0c, upper_32_bits(fifoch->user->vinst)); nv_wo32(fifoch->ramfc, 0x10, 0x0000face); nv_wo32(fifoch->ramfc, 0x30, 0xfffff902); nv_wo32(fifoch->ramfc, 0x48, lower_32_bits(ib_virt)); @@ -223,11 +194,7 @@ nvc0_fifo_destroy_context(struct nouveau_channel *chan) return; nouveau_gpuobj_ref(NULL, &fifoch->ramfc); - if (fifoch->user) { - nouveau_bo_unmap(fifoch->user); - nouveau_bo_unpin(fifoch->user); - nouveau_bo_ref(NULL, &fifoch->user); - } + nouveau_gpuobj_ref(NULL, &fifoch->user); kfree(fifoch); } @@ -240,6 +207,21 @@ nvc0_fifo_load_context(struct nouveau_channel *chan) int nvc0_fifo_unload_context(struct drm_device *dev) { + int i; + + for (i = 0; i < 128; i++) { + if (!(nv_rd32(dev, 0x003004 + (i * 4)) & 1)) + continue; + + nv_mask(dev, 0x003004 + (i * 4), 0x00000001, 0x00000000); + nv_wr32(dev, 0x002634, i); + if (!nv_wait(dev, 0x002634, 0xffffffff, i)) { + NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n", + i, nv_rd32(dev, 0x002634)); + return -EBUSY; + } + } + return 0; } @@ -309,6 +291,7 @@ nvc0_fifo_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; + struct nouveau_channel *chan; struct nvc0_fifo_priv *priv; int ret, i; @@ -351,23 +334,74 @@ nvc0_fifo_init(struct drm_device *dev) nv_wr32(dev, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */ nv_wr32(dev, 0x002100, 0xffffffff); nv_wr32(dev, 0x002140, 0xbfffffff); + + /* restore PFIFO context table */ + for (i = 0; i < 128; i++) { + chan = dev_priv->channels.ptr[i]; + if (!chan || !chan->fifo_priv) + continue; + + nv_wr32(dev, 0x003000 + (i * 8), 0xc0000000 | + (chan->ramin->vinst >> 12)); + nv_wr32(dev, 0x003004 + (i * 8), 0x001f0001); + } + nvc0_fifo_playlist_update(dev); + return 0; } struct nouveau_enum nvc0_fifo_fault_unit[] = { - { 0, "PGRAPH" }, - { 3, "PEEPHOLE" }, - { 4, "BAR1" }, - { 5, "BAR3" }, - { 7, "PFIFO" }, + { 0x00, "PGRAPH" }, + { 0x03, "PEEPHOLE" }, + { 0x04, "BAR1" }, + { 0x05, "BAR3" }, + { 0x07, "PFIFO" }, + { 0x10, "PBSP" }, + { 0x11, "PPPP" }, + { 0x13, "PCOUNTER" }, + { 0x14, "PVP" }, + { 0x15, "PCOPY0" }, + { 0x16, "PCOPY1" }, + { 0x17, "PDAEMON" }, {} }; struct nouveau_enum nvc0_fifo_fault_reason[] = { - { 0, "PT_NOT_PRESENT" }, - { 1, "PT_TOO_SHORT" }, - { 2, "PAGE_NOT_PRESENT" }, - { 3, "VM_LIMIT_EXCEEDED" }, + { 0x00, "PT_NOT_PRESENT" }, + { 0x01, "PT_TOO_SHORT" }, + { 0x02, "PAGE_NOT_PRESENT" }, + { 0x03, "VM_LIMIT_EXCEEDED" }, + { 0x04, "NO_CHANNEL" }, + { 0x05, "PAGE_SYSTEM_ONLY" }, + { 0x06, "PAGE_READ_ONLY" }, + { 0x0a, "COMPRESSED_SYSRAM" }, + { 0x0c, "INVALID_STORAGE_TYPE" }, + {} +}; + +struct nouveau_enum nvc0_fifo_fault_hubclient[] = { + { 0x01, "PCOPY0" }, + { 0x02, "PCOPY1" }, + { 0x04, "DISPATCH" }, + { 0x05, "CTXCTL" }, + { 0x06, "PFIFO" }, + { 0x07, "BAR_READ" }, + { 0x08, "BAR_WRITE" }, + { 0x0b, "PVP" }, + { 0x0c, "PPPP" }, + { 0x0d, "PBSP" }, + { 0x11, "PCOUNTER" }, + { 0x12, "PDAEMON" }, + { 0x14, "CCACHE" }, + { 0x15, "CCACHE_POST" }, + {} +}; + +struct nouveau_enum nvc0_fifo_fault_gpcclient[] = { + { 0x01, "TEX" }, + { 0x0c, "ESETUP" }, + { 0x0e, "CTXCTL" }, + { 0x0f, "PROP" }, {} }; @@ -385,12 +419,20 @@ nvc0_fifo_isr_vm_fault(struct drm_device *dev, int unit) u32 valo = nv_rd32(dev, 0x2804 + (unit * 0x10)); u32 vahi = nv_rd32(dev, 0x2808 + (unit * 0x10)); u32 stat = nv_rd32(dev, 0x280c + (unit * 0x10)); + u32 client = (stat & 0x00001f00) >> 8; NV_INFO(dev, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo); nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f); printk("] from "); nouveau_enum_print(nvc0_fifo_fault_unit, unit); + if (stat & 0x00000040) { + printk("/"); + nouveau_enum_print(nvc0_fifo_fault_hubclient, client); + } else { + printk("/GPC%d/", (stat & 0x1f000000) >> 24); + nouveau_enum_print(nvc0_fifo_fault_gpcclient, client); + } printk(" on channel 0x%010llx\n", (u64)inst << 12); } diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index 3de9b721d8db..ca6db204d644 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -30,27 +30,40 @@ #include "nouveau_mm.h" #include "nvc0_graph.h" -static void nvc0_graph_isr(struct drm_device *); -static void nvc0_runk140_isr(struct drm_device *); -static int nvc0_graph_unload_context_to(struct drm_device *dev, u64 chan); - -void -nvc0_graph_fifo_access(struct drm_device *dev, bool enabled) +static int +nvc0_graph_load_context(struct nouveau_channel *chan) { + struct drm_device *dev = chan->dev; + + nv_wr32(dev, 0x409840, 0x00000030); + nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12); + nv_wr32(dev, 0x409504, 0x00000003); + if (!nv_wait(dev, 0x409800, 0x00000010, 0x00000010)) + NV_ERROR(dev, "PGRAPH: load_ctx timeout\n"); + + return 0; } -struct nouveau_channel * -nvc0_graph_channel(struct drm_device *dev) +static int +nvc0_graph_unload_context_to(struct drm_device *dev, u64 chan) { - return NULL; + nv_wr32(dev, 0x409840, 0x00000003); + nv_wr32(dev, 0x409500, 0x80000000 | chan >> 12); + nv_wr32(dev, 0x409504, 0x00000009); + if (!nv_wait(dev, 0x409800, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "PGRAPH: unload_ctx timeout\n"); + return -EBUSY; + } + + return 0; } static int nvc0_graph_construct_context(struct nouveau_channel *chan) { struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv; - struct nvc0_graph_chan *grch = chan->pgraph_ctx; + struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); + struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; struct drm_device *dev = chan->dev; int ret, i; u32 *ctx; @@ -89,9 +102,8 @@ nvc0_graph_construct_context(struct nouveau_channel *chan) static int nvc0_graph_create_context_mmio_list(struct nouveau_channel *chan) { - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv; - struct nvc0_graph_chan *grch = chan->pgraph_ctx; + struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); + struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; struct drm_device *dev = chan->dev; int i = 0, gpc, tp, ret; u32 magic; @@ -158,29 +170,27 @@ nvc0_graph_create_context_mmio_list(struct nouveau_channel *chan) return 0; } -int -nvc0_graph_create_context(struct nouveau_channel *chan) +static int +nvc0_graph_context_new(struct nouveau_channel *chan, int engine) { - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nvc0_graph_priv *priv = pgraph->priv; + struct nvc0_graph_priv *priv = nv_engine(dev, engine); struct nvc0_graph_chan *grch; - struct drm_device *dev = chan->dev; struct nouveau_gpuobj *grctx; int ret, i; - chan->pgraph_ctx = kzalloc(sizeof(*grch), GFP_KERNEL); - if (!chan->pgraph_ctx) + grch = kzalloc(sizeof(*grch), GFP_KERNEL); + if (!grch) return -ENOMEM; - grch = chan->pgraph_ctx; + chan->engctx[NVOBJ_ENGINE_GR] = grch; ret = nouveau_gpuobj_new(dev, NULL, priv->grctx_size, 256, NVOBJ_FLAG_VM | NVOBJ_FLAG_ZERO_ALLOC, &grch->grctx); if (ret) goto error; - chan->ramin_grctx = grch->grctx; grctx = grch->grctx; ret = nvc0_graph_create_context_mmio_list(chan); @@ -200,104 +210,49 @@ nvc0_graph_create_context(struct nouveau_channel *chan) for (i = 0; i < priv->grctx_size; i += 4) nv_wo32(grctx, i, priv->grctx_vals[i / 4]); - nv_wo32(grctx, 0xf4, 0); - nv_wo32(grctx, 0xf8, 0); - nv_wo32(grctx, 0x10, grch->mmio_nr); - nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->vinst)); - nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->vinst)); - nv_wo32(grctx, 0x1c, 1); - nv_wo32(grctx, 0x20, 0); - nv_wo32(grctx, 0x28, 0); - nv_wo32(grctx, 0x2c, 0); + nv_wo32(grctx, 0xf4, 0); + nv_wo32(grctx, 0xf8, 0); + nv_wo32(grctx, 0x10, grch->mmio_nr); + nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->vinst)); + nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->vinst)); + nv_wo32(grctx, 0x1c, 1); + nv_wo32(grctx, 0x20, 0); + nv_wo32(grctx, 0x28, 0); + nv_wo32(grctx, 0x2c, 0); pinstmem->flush(dev); return 0; error: - pgraph->destroy_context(chan); + priv->base.context_del(chan, engine); return ret; } -void -nvc0_graph_destroy_context(struct nouveau_channel *chan) +static void +nvc0_graph_context_del(struct nouveau_channel *chan, int engine) { - struct nvc0_graph_chan *grch; - - grch = chan->pgraph_ctx; - chan->pgraph_ctx = NULL; - if (!grch) - return; + struct nvc0_graph_chan *grch = chan->engctx[engine]; nouveau_gpuobj_ref(NULL, &grch->mmio); nouveau_gpuobj_ref(NULL, &grch->unk418810); nouveau_gpuobj_ref(NULL, &grch->unk40800c); nouveau_gpuobj_ref(NULL, &grch->unk408004); nouveau_gpuobj_ref(NULL, &grch->grctx); - chan->ramin_grctx = NULL; + chan->engctx[engine] = NULL; } -int -nvc0_graph_load_context(struct nouveau_channel *chan) +static int +nvc0_graph_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) { - struct drm_device *dev = chan->dev; - - nv_wr32(dev, 0x409840, 0x00000030); - nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12); - nv_wr32(dev, 0x409504, 0x00000003); - if (!nv_wait(dev, 0x409800, 0x00000010, 0x00000010)) - NV_ERROR(dev, "PGRAPH: load_ctx timeout\n"); - return 0; } static int -nvc0_graph_unload_context_to(struct drm_device *dev, u64 chan) +nvc0_graph_fini(struct drm_device *dev, int engine) { - nv_wr32(dev, 0x409840, 0x00000003); - nv_wr32(dev, 0x409500, 0x80000000 | chan >> 12); - nv_wr32(dev, 0x409504, 0x00000009); - if (!nv_wait(dev, 0x409800, 0x00000001, 0x00000000)) { - NV_ERROR(dev, "PGRAPH: unload_ctx timeout\n"); - return -EBUSY; - } - return 0; } -int -nvc0_graph_unload_context(struct drm_device *dev) -{ - u64 inst = (u64)(nv_rd32(dev, 0x409b00) & 0x0fffffff) << 12; - return nvc0_graph_unload_context_to(dev, inst); -} - -static void -nvc0_graph_destroy(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nvc0_graph_priv *priv; - - priv = pgraph->priv; - if (!priv) - return; - - nouveau_irq_unregister(dev, 12); - nouveau_irq_unregister(dev, 25); - - nouveau_gpuobj_ref(NULL, &priv->unk4188b8); - nouveau_gpuobj_ref(NULL, &priv->unk4188b4); - - if (priv->grctx_vals) - kfree(priv->grctx_vals); - kfree(priv); -} - -void -nvc0_graph_takedown(struct drm_device *dev) -{ - nvc0_graph_destroy(dev); -} - static int nvc0_graph_mthd_page_flip(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) @@ -306,119 +261,10 @@ nvc0_graph_mthd_page_flip(struct nouveau_channel *chan, return 0; } -static int -nvc0_graph_create(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nvc0_graph_priv *priv; - int ret, gpc, i; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - pgraph->priv = priv; - - ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4); - if (ret) - goto error; - - ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b8); - if (ret) - goto error; - - for (i = 0; i < 0x1000; i += 4) { - nv_wo32(priv->unk4188b4, i, 0x00000010); - nv_wo32(priv->unk4188b8, i, 0x00000010); - } - - priv->gpc_nr = nv_rd32(dev, 0x409604) & 0x0000001f; - priv->rop_nr = (nv_rd32(dev, 0x409604) & 0x001f0000) >> 16; - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - priv->tp_nr[gpc] = nv_rd32(dev, GPC_UNIT(gpc, 0x2608)); - priv->tp_total += priv->tp_nr[gpc]; - } - - /*XXX: these need figuring out... */ - switch (dev_priv->chipset) { - case 0xc0: - if (priv->tp_total == 11) { /* 465, 3/4/4/0, 4 */ - priv->magic_not_rop_nr = 0x07; - /* filled values up to tp_total, the rest 0 */ - priv->magicgpc980[0] = 0x22111000; - priv->magicgpc980[1] = 0x00000233; - priv->magicgpc980[2] = 0x00000000; - priv->magicgpc980[3] = 0x00000000; - priv->magicgpc918 = 0x000ba2e9; - } else - if (priv->tp_total == 14) { /* 470, 3/3/4/4, 5 */ - priv->magic_not_rop_nr = 0x05; - priv->magicgpc980[0] = 0x11110000; - priv->magicgpc980[1] = 0x00233222; - priv->magicgpc980[2] = 0x00000000; - priv->magicgpc980[3] = 0x00000000; - priv->magicgpc918 = 0x00092493; - } else - if (priv->tp_total == 15) { /* 480, 3/4/4/4, 6 */ - priv->magic_not_rop_nr = 0x06; - priv->magicgpc980[0] = 0x11110000; - priv->magicgpc980[1] = 0x03332222; - priv->magicgpc980[2] = 0x00000000; - priv->magicgpc980[3] = 0x00000000; - priv->magicgpc918 = 0x00088889; - } - break; - case 0xc3: /* 450, 4/0/0/0, 2 */ - priv->magic_not_rop_nr = 0x03; - priv->magicgpc980[0] = 0x00003210; - priv->magicgpc980[1] = 0x00000000; - priv->magicgpc980[2] = 0x00000000; - priv->magicgpc980[3] = 0x00000000; - priv->magicgpc918 = 0x00200000; - break; - case 0xc4: /* 460, 3/4/0/0, 4 */ - priv->magic_not_rop_nr = 0x01; - priv->magicgpc980[0] = 0x02321100; - priv->magicgpc980[1] = 0x00000000; - priv->magicgpc980[2] = 0x00000000; - priv->magicgpc980[3] = 0x00000000; - priv->magicgpc918 = 0x00124925; - break; - } - - if (!priv->magic_not_rop_nr) { - NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n", - priv->tp_nr[0], priv->tp_nr[1], priv->tp_nr[2], - priv->tp_nr[3], priv->rop_nr); - /* use 0xc3's values... */ - priv->magic_not_rop_nr = 0x03; - priv->magicgpc980[0] = 0x00003210; - priv->magicgpc980[1] = 0x00000000; - priv->magicgpc980[2] = 0x00000000; - priv->magicgpc980[3] = 0x00000000; - priv->magicgpc918 = 0x00200000; - } - - nouveau_irq_register(dev, 12, nvc0_graph_isr); - nouveau_irq_register(dev, 25, nvc0_runk140_isr); - NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */ - NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */ - NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip); - NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */ - NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */ - return 0; - -error: - nvc0_graph_destroy(dev); - return ret; -} - static void nvc0_graph_init_obj418880(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - struct nvc0_graph_priv *priv = pgraph->priv; + struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); int i; nv_wr32(dev, GPC_BCAST(0x0880), 0x00000000); @@ -449,35 +295,42 @@ nvc0_graph_init_regs(struct drm_device *dev) static void nvc0_graph_init_gpc_0(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv; - int gpc; - - // TP ROP UNKVAL(magic_not_rop_nr) - // 450: 4/0/0/0 2 3 - // 460: 3/4/0/0 4 1 - // 465: 3/4/4/0 4 7 - // 470: 3/3/4/4 5 5 - // 480: 3/4/4/4 6 6 - - // magicgpc918 - // 450: 00200000 00000000001000000000000000000000 - // 460: 00124925 00000000000100100100100100100101 - // 465: 000ba2e9 00000000000010111010001011101001 - // 470: 00092493 00000000000010010010010010010011 - // 480: 00088889 00000000000010001000100010001001 - - /* filled values up to tp_total, remainder 0 */ - // 450: 00003210 00000000 00000000 00000000 - // 460: 02321100 00000000 00000000 00000000 - // 465: 22111000 00000233 00000000 00000000 - // 470: 11110000 00233222 00000000 00000000 - // 480: 11110000 03332222 00000000 00000000 - - nv_wr32(dev, GPC_BCAST(0x0980), priv->magicgpc980[0]); - nv_wr32(dev, GPC_BCAST(0x0984), priv->magicgpc980[1]); - nv_wr32(dev, GPC_BCAST(0x0988), priv->magicgpc980[2]); - nv_wr32(dev, GPC_BCAST(0x098c), priv->magicgpc980[3]); + struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + u32 data[TP_MAX / 8]; + u8 tpnr[GPC_MAX]; + int i, gpc, tpc; + + /* + * TP ROP UNKVAL(magic_not_rop_nr) + * 450: 4/0/0/0 2 3 + * 460: 3/4/0/0 4 1 + * 465: 3/4/4/0 4 7 + * 470: 3/3/4/4 5 5 + * 480: 3/4/4/4 6 6 + * + * magicgpc918 + * 450: 00200000 00000000001000000000000000000000 + * 460: 00124925 00000000000100100100100100100101 + * 465: 000ba2e9 00000000000010111010001011101001 + * 470: 00092493 00000000000010010010010010010011 + * 480: 00088889 00000000000010001000100010001001 + */ + + memset(data, 0x00, sizeof(data)); + memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr)); + for (i = 0, gpc = -1; i < priv->tp_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpnr[gpc]); + tpc = priv->tp_nr[gpc] - tpnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(dev, GPC_BCAST(0x0980), data[0]); + nv_wr32(dev, GPC_BCAST(0x0984), data[1]); + nv_wr32(dev, GPC_BCAST(0x0988), data[2]); + nv_wr32(dev, GPC_BCAST(0x098c), data[3]); for (gpc = 0; gpc < priv->gpc_nr; gpc++) { nv_wr32(dev, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 | @@ -509,8 +362,7 @@ nvc0_graph_init_units(struct drm_device *dev) static void nvc0_graph_init_gpc_1(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv; + struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); int gpc, tp; for (gpc = 0; gpc < priv->gpc_nr; gpc++) { @@ -535,8 +387,7 @@ nvc0_graph_init_gpc_1(struct drm_device *dev) static void nvc0_graph_init_rop(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv; + struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); int rop; for (rop = 0; rop < priv->rop_nr; rop++) { @@ -547,62 +398,36 @@ nvc0_graph_init_rop(struct drm_device *dev) } } -static int -nvc0_fuc_load_fw(struct drm_device *dev, u32 fuc_base, - const char *code_fw, const char *data_fw) +static void +nvc0_graph_init_fuc(struct drm_device *dev, u32 fuc_base, + struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data) { - const struct firmware *fw; - char name[32]; - int ret, i; - - snprintf(name, sizeof(name), "nouveau/%s", data_fw); - ret = request_firmware(&fw, name, &dev->pdev->dev); - if (ret) { - NV_ERROR(dev, "failed to load %s\n", data_fw); - return ret; - } + int i; nv_wr32(dev, fuc_base + 0x01c0, 0x01000000); - for (i = 0; i < fw->size / 4; i++) - nv_wr32(dev, fuc_base + 0x01c4, ((u32 *)fw->data)[i]); - release_firmware(fw); - - snprintf(name, sizeof(name), "nouveau/%s", code_fw); - ret = request_firmware(&fw, name, &dev->pdev->dev); - if (ret) { - NV_ERROR(dev, "failed to load %s\n", code_fw); - return ret; - } + for (i = 0; i < data->size / 4; i++) + nv_wr32(dev, fuc_base + 0x01c4, data->data[i]); nv_wr32(dev, fuc_base + 0x0180, 0x01000000); - for (i = 0; i < fw->size / 4; i++) { + for (i = 0; i < code->size / 4; i++) { if ((i & 0x3f) == 0) nv_wr32(dev, fuc_base + 0x0188, i >> 6); - nv_wr32(dev, fuc_base + 0x0184, ((u32 *)fw->data)[i]); + nv_wr32(dev, fuc_base + 0x0184, code->data[i]); } - release_firmware(fw); - - return 0; } static int nvc0_graph_init_ctxctl(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv; + struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); u32 r000260; - int ret; /* load fuc microcode */ r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000); - ret = nvc0_fuc_load_fw(dev, 0x409000, "fuc409c", "fuc409d"); - if (ret == 0) - ret = nvc0_fuc_load_fw(dev, 0x41a000, "fuc41ac", "fuc41ad"); + nvc0_graph_init_fuc(dev, 0x409000, &priv->fuc409c, &priv->fuc409d); + nvc0_graph_init_fuc(dev, 0x41a000, &priv->fuc41ac, &priv->fuc41ad); nv_wr32(dev, 0x000260, r000260); - if (ret) - return ret; - /* start both of them running */ nv_wr32(dev, 0x409840, 0xffffffff); nv_wr32(dev, 0x41a10c, 0x00000000); @@ -644,41 +469,19 @@ nvc0_graph_init_ctxctl(struct drm_device *dev) return 0; } -int -nvc0_graph_init(struct drm_device *dev) +static int +nvc0_graph_init(struct drm_device *dev, int engine) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; int ret; - dev_priv->engine.graph.accel_blocked = true; - - switch (dev_priv->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - break; - default: - NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n"); - if (nouveau_noaccel != 0) - return 0; - break; - } - nv_mask(dev, 0x000200, 0x18001000, 0x00000000); nv_mask(dev, 0x000200, 0x18001000, 0x18001000); - if (!pgraph->priv) { - ret = nvc0_graph_create(dev); - if (ret) - return ret; - } - nvc0_graph_init_obj418880(dev); nvc0_graph_init_regs(dev); - //nvc0_graph_init_unitplemented_magics(dev); + /*nvc0_graph_init_unitplemented_magics(dev);*/ nvc0_graph_init_gpc_0(dev); - //nvc0_graph_init_unitplemented_c242(dev); + /*nvc0_graph_init_unitplemented_c242(dev);*/ nv_wr32(dev, 0x400500, 0x00010001); nv_wr32(dev, 0x400100, 0xffffffff); @@ -697,12 +500,13 @@ nvc0_graph_init(struct drm_device *dev) nv_wr32(dev, 0x400054, 0x34ce3464); ret = nvc0_graph_init_ctxctl(dev); - if (ret == 0) - dev_priv->engine.graph.accel_blocked = false; + if (ret) + return ret; + return 0; } -static int +int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -806,3 +610,187 @@ nvc0_runk140_isr(struct drm_device *dev) units &= ~(1 << unit); } } + +static int +nvc0_graph_create_fw(struct drm_device *dev, const char *fwname, + struct nvc0_graph_fuc *fuc) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + const struct firmware *fw; + char f[32]; + int ret; + + snprintf(f, sizeof(f), "nouveau/nv%02x_%s", dev_priv->chipset, fwname); + ret = request_firmware(&fw, f, &dev->pdev->dev); + if (ret) { + snprintf(f, sizeof(f), "nouveau/%s", fwname); + ret = request_firmware(&fw, f, &dev->pdev->dev); + if (ret) { + NV_ERROR(dev, "failed to load %s\n", fwname); + return ret; + } + } + + fuc->size = fw->size; + fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL); + release_firmware(fw); + return (fuc->data != NULL) ? 0 : -ENOMEM; +} + +static void +nvc0_graph_destroy_fw(struct nvc0_graph_fuc *fuc) +{ + if (fuc->data) { + kfree(fuc->data); + fuc->data = NULL; + } +} + +static void +nvc0_graph_destroy(struct drm_device *dev, int engine) +{ + struct nvc0_graph_priv *priv = nv_engine(dev, engine); + + nvc0_graph_destroy_fw(&priv->fuc409c); + nvc0_graph_destroy_fw(&priv->fuc409d); + nvc0_graph_destroy_fw(&priv->fuc41ac); + nvc0_graph_destroy_fw(&priv->fuc41ad); + + nouveau_irq_unregister(dev, 12); + nouveau_irq_unregister(dev, 25); + + nouveau_gpuobj_ref(NULL, &priv->unk4188b8); + nouveau_gpuobj_ref(NULL, &priv->unk4188b4); + + if (priv->grctx_vals) + kfree(priv->grctx_vals); + + NVOBJ_ENGINE_DEL(dev, GR); + kfree(priv); +} + +int +nvc0_graph_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_graph_priv *priv; + int ret, gpc, i; + + switch (dev_priv->chipset) { + case 0xc0: + case 0xc3: + case 0xc4: + break; + default: + NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n"); + return 0; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.destroy = nvc0_graph_destroy; + priv->base.init = nvc0_graph_init; + priv->base.fini = nvc0_graph_fini; + priv->base.context_new = nvc0_graph_context_new; + priv->base.context_del = nvc0_graph_context_del; + priv->base.object_new = nvc0_graph_object_new; + + NVOBJ_ENGINE_ADD(dev, GR, &priv->base); + nouveau_irq_register(dev, 12, nvc0_graph_isr); + nouveau_irq_register(dev, 25, nvc0_runk140_isr); + + if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) || + nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) || + nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) || + nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) { + ret = 0; + goto error; + } + + + ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4); + if (ret) + goto error; + + ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b8); + if (ret) + goto error; + + for (i = 0; i < 0x1000; i += 4) { + nv_wo32(priv->unk4188b4, i, 0x00000010); + nv_wo32(priv->unk4188b8, i, 0x00000010); + } + + priv->gpc_nr = nv_rd32(dev, 0x409604) & 0x0000001f; + priv->rop_nr = (nv_rd32(dev, 0x409604) & 0x001f0000) >> 16; + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + priv->tp_nr[gpc] = nv_rd32(dev, GPC_UNIT(gpc, 0x2608)); + priv->tp_total += priv->tp_nr[gpc]; + } + + /*XXX: these need figuring out... */ + switch (dev_priv->chipset) { + case 0xc0: + if (priv->tp_total == 11) { /* 465, 3/4/4/0, 4 */ + priv->magic_not_rop_nr = 0x07; + /* filled values up to tp_total, the rest 0 */ + priv->magicgpc918 = 0x000ba2e9; + } else + if (priv->tp_total == 14) { /* 470, 3/3/4/4, 5 */ + priv->magic_not_rop_nr = 0x05; + priv->magicgpc918 = 0x00092493; + } else + if (priv->tp_total == 15) { /* 480, 3/4/4/4, 6 */ + priv->magic_not_rop_nr = 0x06; + priv->magicgpc918 = 0x00088889; + } + break; + case 0xc3: /* 450, 4/0/0/0, 2 */ + priv->magic_not_rop_nr = 0x03; + priv->magicgpc918 = 0x00200000; + break; + case 0xc4: /* 460, 3/4/0/0, 4 */ + priv->magic_not_rop_nr = 0x01; + priv->magicgpc918 = 0x00124925; + break; + } + + if (!priv->magic_not_rop_nr) { + NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n", + priv->tp_nr[0], priv->tp_nr[1], priv->tp_nr[2], + priv->tp_nr[3], priv->rop_nr); + /* use 0xc3's values... */ + priv->magic_not_rop_nr = 0x03; + priv->magicgpc918 = 0x00200000; + } + + NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */ + NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */ + NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip); + NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */ + NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */ + return 0; + +error: + nvc0_graph_destroy(dev, NVOBJ_ENGINE_GR); + return ret; +} + +MODULE_FIRMWARE("nouveau/nvc0_fuc409c"); +MODULE_FIRMWARE("nouveau/nvc0_fuc409d"); +MODULE_FIRMWARE("nouveau/nvc0_fuc41ac"); +MODULE_FIRMWARE("nouveau/nvc0_fuc41ad"); +MODULE_FIRMWARE("nouveau/nvc3_fuc409c"); +MODULE_FIRMWARE("nouveau/nvc3_fuc409d"); +MODULE_FIRMWARE("nouveau/nvc3_fuc41ac"); +MODULE_FIRMWARE("nouveau/nvc3_fuc41ad"); +MODULE_FIRMWARE("nouveau/nvc4_fuc409c"); +MODULE_FIRMWARE("nouveau/nvc4_fuc409d"); +MODULE_FIRMWARE("nouveau/nvc4_fuc41ac"); +MODULE_FIRMWARE("nouveau/nvc4_fuc41ad"); +MODULE_FIRMWARE("nouveau/fuc409c"); +MODULE_FIRMWARE("nouveau/fuc409d"); +MODULE_FIRMWARE("nouveau/fuc41ac"); +MODULE_FIRMWARE("nouveau/fuc41ad"); diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h index 40e26f9c56c4..f5d184e0689d 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.h +++ b/drivers/gpu/drm/nouveau/nvc0_graph.h @@ -28,13 +28,25 @@ #define GPC_MAX 4 #define TP_MAX 32 -#define ROP_BCAST(r) (0x408800 + (r)) -#define ROP_UNIT(u,r) (0x410000 + (u) * 0x400 + (r)) -#define GPC_BCAST(r) (0x418000 + (r)) -#define GPC_UNIT(t,r) (0x500000 + (t) * 0x8000 + (r)) -#define TP_UNIT(t,m,r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r)) +#define ROP_BCAST(r) (0x408800 + (r)) +#define ROP_UNIT(u, r) (0x410000 + (u) * 0x400 + (r)) +#define GPC_BCAST(r) (0x418000 + (r)) +#define GPC_UNIT(t, r) (0x500000 + (t) * 0x8000 + (r)) +#define TP_UNIT(t, m, r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r)) + +struct nvc0_graph_fuc { + u32 *data; + u32 size; +}; struct nvc0_graph_priv { + struct nouveau_exec_engine base; + + struct nvc0_graph_fuc fuc409c; + struct nvc0_graph_fuc fuc409d; + struct nvc0_graph_fuc fuc41ac; + struct nvc0_graph_fuc fuc41ad; + u8 gpc_nr; u8 rop_nr; u8 tp_nr[GPC_MAX]; @@ -46,15 +58,14 @@ struct nvc0_graph_priv { struct nouveau_gpuobj *unk4188b8; u8 magic_not_rop_nr; - u32 magicgpc980[4]; u32 magicgpc918; }; struct nvc0_graph_chan { struct nouveau_gpuobj *grctx; - struct nouveau_gpuobj *unk408004; // 0x418810 too - struct nouveau_gpuobj *unk40800c; // 0x419004 too - struct nouveau_gpuobj *unk418810; // 0x419848 too + struct nouveau_gpuobj *unk408004; /* 0x418810 too */ + struct nouveau_gpuobj *unk40800c; /* 0x419004 too */ + struct nouveau_gpuobj *unk418810; /* 0x419848 too */ struct nouveau_gpuobj *mmio; int mmio_nr; }; diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c index f880ff776db8..6df066114133 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grctx.c +++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c @@ -1623,7 +1623,7 @@ nvc0_grctx_generate_rop(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - // ROPC_BROADCAST + /* ROPC_BROADCAST */ nv_wr32(dev, 0x408800, 0x02802a3c); nv_wr32(dev, 0x408804, 0x00000040); nv_wr32(dev, 0x408808, 0x0003e00d); @@ -1647,7 +1647,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) { int i; - // GPC_BROADCAST + /* GPC_BROADCAST */ nv_wr32(dev, 0x418380, 0x00000016); nv_wr32(dev, 0x418400, 0x38004e00); nv_wr32(dev, 0x418404, 0x71e0ffff); @@ -1728,7 +1728,7 @@ nvc0_grctx_generate_tp(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - // GPC_BROADCAST.TP_BROADCAST + /* GPC_BROADCAST.TP_BROADCAST */ nv_wr32(dev, 0x419848, 0x00000000); nv_wr32(dev, 0x419864, 0x0000012a); nv_wr32(dev, 0x419888, 0x00000000); @@ -1741,7 +1741,7 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419a1c, 0x00000000); nv_wr32(dev, 0x419a20, 0x00000800); if (dev_priv->chipset != 0xc0) - nv_wr32(dev, 0x00419ac4, 0x0007f440); // 0xc3 + nv_wr32(dev, 0x00419ac4, 0x0007f440); /* 0xc3 */ nv_wr32(dev, 0x419b00, 0x0a418820); nv_wr32(dev, 0x419b04, 0x062080e6); nv_wr32(dev, 0x419b08, 0x020398a4); @@ -1797,8 +1797,8 @@ int nvc0_grctx_generate(struct nouveau_channel *chan) { struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv; - struct nvc0_graph_chan *grch = chan->pgraph_ctx; + struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); + struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; struct drm_device *dev = chan->dev; int i, gpc, tp, id; u32 r000260, tmp; @@ -1912,13 +1912,13 @@ nvc0_grctx_generate(struct nouveau_channel *chan) for (i = 1; i < 7; i++) data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); - // GPC_BROADCAST + /* GPC_BROADCAST */ nv_wr32(dev, 0x418bb8, (priv->tp_total << 8) | priv->magic_not_rop_nr); for (i = 0; i < 6; i++) nv_wr32(dev, 0x418b08 + (i * 4), data[i]); - // GPC_BROADCAST.TP_BROADCAST + /* GPC_BROADCAST.TP_BROADCAST */ nv_wr32(dev, 0x419bd0, (priv->tp_total << 8) | priv->magic_not_rop_nr | data2[0]); @@ -1926,7 +1926,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) for (i = 0; i < 6; i++) nv_wr32(dev, 0x419b00 + (i * 4), data[i]); - // UNK78xx + /* UNK78xx */ nv_wr32(dev, 0x4078bc, (priv->tp_total << 8) | priv->magic_not_rop_nr); for (i = 0; i < 6; i++) @@ -1944,7 +1944,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) gpc = -1; for (i = 0, gpc = -1; i < 32; i++) { int ltp = i * (priv->tp_total - 1) / 32; - + do { gpc = (gpc + 1) % priv->gpc_nr; } while (!tpnr[gpc]); |