diff --git a/packages/linux/patches/linux-2.6.39-rc6-110-drm_nouveau_upstream-20110509.patch b/packages/linux/patches/linux-2.6.39-rc6-110-drm_nouveau_upstream-20110509.patch new file mode 100644 index 0000000000..2572644720 --- /dev/null +++ b/packages/linux/patches/linux-2.6.39-rc6-110-drm_nouveau_upstream-20110509.patch @@ -0,0 +1,9012 @@ +diff -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/Makefile linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/Makefile +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/Makefile 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/Makefile 2011-05-09 00:36:22.000000000 +0200 +@@ -20,6 +20,8 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_bios.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_bios.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.c 2011-05-09 00:36:22.000000000 +0200 +@@ -5049,11 +5049,7 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_bios.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_bios.h 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.h 2011-05-09 00:36:22.000000000 +0200 +@@ -82,6 +82,7 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_channel.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_channel.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_channel.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_channel.c 2011-05-09 00:36:22.000000000 +0200 +@@ -268,9 +268,8 @@ + 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 @@ + /* 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_connector.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_connector.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_connector.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_connector.c 2011-05-09 00:36:22.000000000 +0200 +@@ -442,7 +442,7 @@ + } + + /* 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_display.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_display.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_display.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_display.c 2011-05-09 00:36:22.000000000 +0200 +@@ -276,7 +276,7 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_drv.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_drv.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_drv.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_drv.c 2011-05-09 00:36:22.000000000 +0200 +@@ -162,11 +162,10 @@ + 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_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 @@ + + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_drv.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_drv.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_drv.h 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_drv.h 2011-05-09 00:36:22.000000000 +0200 +@@ -150,13 +150,12 @@ + + #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_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 @@ + } 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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_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 @@ + 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 @@ + 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 @@ + 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 nouveau_fbdev *nfbdev; + struct apertures_struct *apertures; +- +- bool powered_down; + }; + + static inline struct drm_nouveau_private * +@@ -883,17 +855,27 @@ + 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 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_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 @@ + /* 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_grctx.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_grctx.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_grctx.h 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_grctx.h 2011-05-09 00:36:22.000000000 +0200 +@@ -87,10 +87,10 @@ + 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_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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_mem.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_mem.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_mem.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_mem.c 2011-05-09 00:36:22.000000000 +0200 +@@ -51,8 +51,7 @@ + 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 @@ + 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); +@@ -152,8 +154,6 @@ + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + +- nouveau_bo_ref(NULL, &dev_priv->vga_ram); +- + ttm_bo_device_release(&dev_priv->ttm.bdev); + + nouveau_ttm_global_release(dev_priv); +@@ -597,10 +597,10 @@ + 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]; +@@ -643,51 +643,68 @@ + /* 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, +@@ -695,10 +712,11 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_object.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_object.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_object.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_object.c 2011-05-09 00:36:22.000000000 +0200 +@@ -361,20 +361,6 @@ + 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 @@ + 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 @@ + 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 @@ + 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; +- } ++ struct nouveau_exec_engine *eng = dev_priv->eng[oc->engine]; + +- NV_ERROR(dev, "illegal object class: 0x%x\n", class); +- return -EINVAL; ++ if (oc->id != class) ++ continue; + +-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->engine == NVOBJ_ENGINE_SW) ++ return nouveau_gpuobj_sw_new(chan, handle, class); + +- ret = pgraph->create_context(chan); ++ if (!chan->engctx[oc->engine]) { ++ ret = eng->context_new(chan, oc->engine); + if (ret) + return ret; + } +- break; +- case NVOBJ_ENGINE_CRYPT: +- if (!chan->crypt_ctx) { +- struct nouveau_crypt_engine *pcrypt = +- &dev_priv->engine.crypt; + +- ret = pcrypt->create_context(chan); +- 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; ++ return eng->object_new(chan, oc->engine, handle, class); + } + +- 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 +- } +- } +- } +- 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_perf.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_perf.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_perf.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_perf.c 2011-05-09 00:36:22.000000000 +0200 +@@ -72,6 +72,68 @@ + 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 @@ + 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 @@ + #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 @@ + } + } + ++ /* 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_pm.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_pm.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_pm.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_pm.c 2011-05-09 00:36:22.000000000 +0200 +@@ -156,7 +156,7 @@ + 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 @@ + 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 @@ + #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 @@ + 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 @@ + /* 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_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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_reg.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_reg.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_reg.h 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_reg.h 2011-05-09 00:36:22.000000000 +0200 +@@ -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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_state.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_state.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_state.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_state.c 2011-05-09 00:36:22.000000000 +0200 +@@ -65,14 +65,6 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + { + 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 @@ + 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 @@ + + /* 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_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 @@ + { + 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); +@@ -768,6 +738,11 @@ + engine->mc.takedown(dev); + engine->display.late_takedown(dev); + ++ if (dev_priv->vga_ram) { ++ nouveau_bo_unpin(dev_priv->vga_ram); ++ nouveau_bo_ref(NULL, &dev_priv->vga_ram); ++ } ++ + mutex_lock(&dev->struct_mutex); + ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); + ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT); +@@ -861,7 +836,7 @@ + #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; + } +@@ -913,11 +888,13 @@ + + /* 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_vm.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_vm.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_vm.h 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_vm.h 2011-05-09 00:36:22.000000000 +0200 +@@ -53,8 +53,7 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_volt.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_volt.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nouveau_volt.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nouveau_volt.c 2011-05-09 00:36:22.000000000 +0200 +@@ -159,8 +159,16 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv04_crtc.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv04_crtc.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv04_crtc.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv04_crtc.c 2011-05-09 00:36:22.000000000 +0200 +@@ -790,8 +790,7 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv04_graph.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv04_graph.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv04_graph.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv04_graph.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv04_instmem.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv04_instmem.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv04_instmem.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv04_instmem.c 2011-05-09 00:36:22.000000000 +0200 +@@ -95,6 +95,9 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv10_graph.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv10_graph.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv10_graph.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv10_graph.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 @@ + + 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_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_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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + /* 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 @@ + 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 @@ + + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + } + } + } ++ ++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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv20_graph.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv20_graph.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv20_graph.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv20_graph.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) ++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 nouveau_gpuobj *ctx) + { + int i; + +@@ -87,7 +137,7 @@ + } + + 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 @@ + } + + 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 @@ + } + + 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 @@ + } + + 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 @@ + } + + 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 @@ + } + + 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; +- +- if (!chan->ramin_grctx) +- return -EINVAL; +- inst = chan->ramin_grctx->pinst >> 4; ++ nv_wo32(pgraph->ctxtab, chan->id * 4, 0); + +- 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 @@ + } + + 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 @@ + 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 @@ + 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 @@ + } + } + } ++ ++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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv40_fifo.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv40_fifo.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv40_fifo.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv40_fifo.c 2011-05-09 00:36:22.000000000 +0200 +@@ -115,6 +115,7 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv40_graph.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv40_graph.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv40_graph.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv40_graph.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 @@ + 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 @@ + 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; + } + ++static void ++nv40_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; ++ 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 (nv40_graph_channel(dev) == chan) ++ nv40_graph_unload_context(dev); ++ ++ 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_unload_context(struct drm_device *dev) ++nv40_graph_object_new(struct nouveau_channel *chan, int engine, ++ u32 handle, u16 class) + { +- uint32_t inst; ++ struct drm_device *dev = chan->dev; ++ struct nouveau_gpuobj *obj = NULL; + int ret; + +- inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR); +- if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED)) +- return 0; +- inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE; ++ ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_FREE, &obj); ++ if (ret) ++ return ret; ++ obj->engine = 1; ++ obj->class = class; + +- ret = nv40_graph_transfer_context(dev, inst, 1); ++ 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); + +- nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst); ++ 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 @@ + * 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 @@ + 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 @@ + + 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 @@ + 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 @@ + 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 @@ + } + } + } ++ ++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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv40_mpeg.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv40_mpeg.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv40_mpeg.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv40_mpeg.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_calc.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_calc.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_calc.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_calc.c 2011-05-09 00:36:22.000000000 +0200 +@@ -23,7 +23,6 @@ + */ + + #include "drmP.h" +-#include "drm_fixed.h" + #include "nouveau_drv.h" + #include "nouveau_hw.h" + +@@ -47,45 +46,52 @@ + } + + 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); +- +- /* 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); +- +- /* *N = floor(fb_div); */ +- a.full = dfixed_floor(fb_div); +- *N = dfixed_trunc(fb_div); +- +- /* *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; ++ 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); ++ ++ 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++; ++ ++ if (N < pll->vco1.min_n) ++ continue; ++ if (N > pll->vco1.max_n) ++ break; ++ ++ err = abs(clk - (pll->refclk * N / M / *P)); ++ if (err < best_err) { ++ best_err = err; ++ *pN = N; ++ *pM = M; ++ } ++ ++ 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 clk; ++ return pll->refclk * *pN / *pM / *P; + } +diff -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_crtc.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_crtc.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_crtc.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_crtc.c 2011-05-09 00:36:22.000000000 +0200 +@@ -286,7 +286,7 @@ + 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 @@ + 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 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_display.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_display.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_display.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_display.c 2011-05-09 00:36:22.000000000 +0200 +@@ -517,13 +517,25 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_graph.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_graph.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_graph.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_graph.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 @@ + { + 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 @@ + 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 @@ + 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 @@ + 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 @@ + + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_grctx.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_grctx.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_grctx.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_grctx.c 2011-05-09 00:36:22.000000000 +0200 +@@ -747,7 +747,7 @@ + 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 @@ + 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 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_mpeg.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_mpeg.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_mpeg.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_mpeg.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_pm.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_pm.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_pm.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_pm.c 2011-05-09 00:36:22.000000000 +0200 +@@ -47,6 +47,21 @@ + + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_vm.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_vm.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv50_vm.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv50_vm.c 2011-05-09 00:36:22.000000000 +0200 +@@ -151,8 +151,7 @@ + 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 @@ + } + + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv84_crypt.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv84_crypt.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nv84_crypt.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nv84_crypt.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 @@ + 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 @@ + + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_copy.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_copy.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_copy.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_copy.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 ++#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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_copy.fuc linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_copy.fuc +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_copy.fuc 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_copy.fuc 2011-05-09 00:36:22.000000000 +0200 +@@ -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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_copy.fuc.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_copy.fuc.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_copy.fuc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_copy.fuc.h 2011-05-09 00:36:22.000000000 +0200 +@@ -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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_pm.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_pm.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nva3_pm.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nva3_pm.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 @@ + 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; + +- state = kzalloc(sizeof(*state), GFP_KERNEL); +- if (!state) +- return ERR_PTR(-ENOMEM); +- +- ret = get_pll_limits(dev, id, &state->pll); +- if (ret < 0) { +- kfree(state); ++ 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); ++ ++ ++ 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; ++ ++ /* 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_copy.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_copy.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_copy.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_copy.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 ++#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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h 2011-05-09 00:36:22.000000000 +0200 +@@ -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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_fifo.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_fifo.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_fifo.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_fifo.c 2011-05-09 00:36:22.000000000 +0200 +@@ -37,7 +37,7 @@ + }; + + struct nvc0_fifo_chan { +- struct nouveau_bo *user; ++ struct nouveau_gpuobj *user; + struct nouveau_gpuobj *ramfc; + }; + +@@ -106,7 +106,7 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + { + 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 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_graph.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_graph.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_graph.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_graph.c 2011-05-09 00:36:22.000000000 +0200 +@@ -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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + } + } + +-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 @@ + 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 @@ + 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 @@ + 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_graph.h linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_graph.h +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_graph.h 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_graph.h 2011-05-09 00:36:22.000000000 +0200 +@@ -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 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 -Naur linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_grctx.c linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_grctx.c +--- linux-2.6.39-rc6/drivers/gpu/drm/nouveau/nvc0_grctx.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6.nouveau/drivers/gpu/drm/nouveau/nvc0_grctx.c 2011-05-09 00:36:22.000000000 +0200 +@@ -1623,7 +1623,7 @@ + { + 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 @@ + { + 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 @@ + { + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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]);