From 493f938e0dfc070e13bd66bc683c31fa08233609 Mon Sep 17 00:00:00 2001 From: Gary King Date: Tue, 31 Aug 2010 16:45:55 -0700 Subject: [PATCH] video: tegra: add host1x support to driver includes changes by: Antti Hatala (1) Gary King (1) Erik Gilling (2) Antti Hatala video: tegra: dc: use nvhost driver for host1x power management Gary King video: tegra: fb: add ioctl to flip dc windows to nvmap handles Erik Gilling video: tegra: dc: increment syncpoints following window flips video: tegra: drain syncpt waits on display disable Original commit messages: ** video: tegra: dc: use nvhost driver for host1x power management Incrementing the frame done syncpoint value from the display interrupt requires that the host module is powered on. As the syncpoint state is saved and restored automatically by the host driver a cpu increment of a syncpoint in the powered down state will be lost. Also adds checks for host module being powered. ** video: tegra: fb: add ioctl to flip dc windows to nvmap handles tegra user-space graphics drivers may allocate framebuffers using nvmap rather than rendering to the common framebuffer, this may be done to support deeper buffer pipelining, color formats and pixel layouts other than the initial bootup framebuffer, etc. to use this ioctl, a caller must first specify an nvmap fd which is already open in the calling process so that the subsequent flip ioctls may be properly validated. flips are performed asynchronously, with flip completion notifications provided back to the caller via the host1x syncpoint mechanism based on earlier changes made by Antti Hatala and Erik Gilling Change-Id: I4e8a8bb92085a485d65fd87d89112b2969ee37ff Signed-off-by: Gary King --- arch/arm/mach-tegra/include/mach/dc.h | 7 + drivers/video/tegra/dc/dc.c | 81 +++++-- drivers/video/tegra/dc/dc_priv.h | 18 +- drivers/video/tegra/fb.c | 323 +++++++++++++++++++++++++- include/video/tegrafb.h | 83 +++++++ 5 files changed, 488 insertions(+), 24 deletions(-) create mode 100644 include/video/tegrafb.h diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h index ce562e5634f7..49c09a177d00 100644 --- a/arch/arm/mach-tegra/include/mach/dc.h +++ b/arch/arm/mach-tegra/include/mach/dc.h @@ -86,6 +86,7 @@ struct tegra_dc_out { #define TEGRA_DC_ORDER_BLUE_RED 1 struct tegra_dc; +struct nvmap_handle_ref; struct tegra_dc_win { u8 idx; @@ -108,6 +109,8 @@ struct tegra_dc_win { int dirty; struct tegra_dc *dc; + + struct nvmap_handle_ref *surface; }; #define TEGRA_WIN_FLAG_ENABLED (1 << 0) @@ -165,6 +168,10 @@ struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win); void tegra_dc_enable(struct tegra_dc *dc); void tegra_dc_disable(struct tegra_dc *dc); +u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc); +u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc); +void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, u32 val); + /* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows * with differenct dcs in one call */ diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index fadecf4796e1..16e6a454336d 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -102,6 +102,8 @@ static void _dump_regs(struct tegra_dc *dc, void *data, int i; char buff[256]; + tegra_dc_io_start(dc); + DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); DUMP_REG(DC_CMD_DISPLAY_COMMAND); DUMP_REG(DC_CMD_SIGNAL_RAISE); @@ -225,6 +227,8 @@ static void _dump_regs(struct tegra_dc *dc, void *data, DUMP_REG(DC_WINBUF_ADDR_H_OFFSET); DUMP_REG(DC_WINBUF_ADDR_V_OFFSET); } + + tegra_dc_io_end(dc); } #undef DUMP_REG @@ -285,7 +289,6 @@ static void tegra_dc_dbg_add(struct tegra_dc *dc) snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->ndev->id); (void) debugfs_create_file(name, S_IRUGO, NULL, dc, &dbg_fops); - } #else static void tegra_dc_dbg_add(struct tegra_dc *dc) {} @@ -533,13 +536,40 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) } tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); - mutex_unlock(&dc->lock); return 0; } EXPORT_SYMBOL(tegra_dc_update_windows); +u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc) +{ + return dc->syncpt_id; +} +EXPORT_SYMBOL(tegra_dc_get_syncpt_id); + +u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc) +{ + u32 max; + + mutex_lock(&dc->lock); + max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt, dc->syncpt_id, 1); + dc->syncpt_max = max; + mutex_unlock(&dc->lock); + + return max; +} + +void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, u32 val) +{ + mutex_lock(&dc->lock); + while (dc->syncpt_min < val) { + dc->syncpt_min++; + nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, dc->syncpt_id); + } + mutex_unlock(&dc->lock); +} + static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[], int n) { @@ -769,11 +799,18 @@ static void tegra_dc_set_color_control(struct tegra_dc *dc) static void tegra_dc_init(struct tegra_dc *dc) { + u32 disp_syncpt; + u32 vblank_syncpt; + tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - if (dc->ndev->id == 0) - tegra_dc_writel(dc, 0x0000011a, DC_CMD_CONT_SYNCPT_VSYNC); - else - tegra_dc_writel(dc, 0x0000011b, DC_CMD_CONT_SYNCPT_VSYNC); + if (dc->ndev->id == 0) { + disp_syncpt = NVSYNCPT_DISP0; + vblank_syncpt = NVSYNCPT_VBLANK0; + } else if (dc->ndev->id == 1) { + disp_syncpt = NVSYNCPT_DISP1; + vblank_syncpt = NVSYNCPT_VBLANK1; + } + tegra_dc_writel(dc, 0x00000100 | vblank_syncpt, DC_CMD_CONT_SYNCPT_VSYNC); tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE); tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY); tegra_dc_writel(dc, 0x00000020, DC_DISP_MEM_HIGH_PRIORITY); @@ -786,18 +823,24 @@ static void tegra_dc_init(struct tegra_dc *dc) tegra_dc_set_color_control(dc); + dc->syncpt_id = disp_syncpt; + + dc->syncpt_min = dc->syncpt_max = + nvhost_syncpt_read(&dc->ndev->host->syncpt, disp_syncpt); + if (dc->mode.pclk) tegra_dc_program_mode(dc, &dc->mode); } static void _tegra_dc_enable(struct tegra_dc *dc) { + tegra_dc_io_start(dc); + if (dc->out && dc->out->enable) dc->out->enable(); tegra_dc_setup_clk(dc, dc->clk); - clk_enable(dc->host1x_clk); clk_enable(dc->clk); tegra_periph_reset_deassert(dc->clk); enable_irq(dc->irq); @@ -831,10 +874,17 @@ static void _tegra_dc_disable(struct tegra_dc *dc) disable_irq(dc->irq); tegra_periph_reset_assert(dc->clk); clk_disable(dc->clk); - clk_disable(dc->host1x_clk); if (dc->out && dc->out->disable) dc->out->disable(); + + /* flush any pending syncpt waits */ + while (dc->syncpt_min < dc->syncpt_max) { + dc->syncpt_min++; + nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, dc->syncpt_id); + } + + tegra_dc_io_end(dc); } @@ -854,7 +904,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev) { struct tegra_dc *dc; struct clk *clk; - struct clk *host1x_clk; struct resource *res; struct resource *base_res; struct resource *fb_mem = NULL; @@ -904,23 +953,14 @@ static int tegra_dc_probe(struct nvhost_device *ndev) fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem"); - host1x_clk = clk_get(&ndev->dev, "host1x"); - if (IS_ERR_OR_NULL(host1x_clk)) { - dev_err(&ndev->dev, "can't get host1x clock\n"); - ret = -ENOENT; - goto err_iounmap_reg; - } - clk = clk_get(&ndev->dev, NULL); if (IS_ERR_OR_NULL(clk)) { dev_err(&ndev->dev, "can't get clock\n"); ret = -ENOENT; - - goto err_put_host1x_clk; + goto err_iounmap_reg; } dc->clk = clk; - dc->host1x_clk = host1x_clk; dc->base_res = base_res; dc->base = base; dc->irq = irq; @@ -984,8 +1024,6 @@ err_free_irq: free_irq(irq, dc); err_put_clk: clk_put(clk); -err_put_host1x_clk: - clk_put(host1x_clk); err_iounmap_reg: iounmap(base); if (fb_mem) @@ -1014,7 +1052,6 @@ static int tegra_dc_remove(struct nvhost_device *ndev) free_irq(dc->irq, dc); clk_put(dc->clk); - clk_put(dc->host1x_clk); iounmap(dc->base); if (dc->fb_mem) release_resource(dc->base_res); diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index 2297be40f030..39a03e8fb9c0 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -22,6 +22,7 @@ #include #include #include +#include "../host/dev.h" struct tegra_dc; @@ -54,7 +55,6 @@ struct tegra_dc { int irq; struct clk *clk; - struct clk *host1x_clk; bool enabled; @@ -74,17 +74,33 @@ struct tegra_dc { struct resource *fb_mem; struct tegra_fb_info *fb; + + u32 syncpt_id; + u32 syncpt_min; + u32 syncpt_max; }; +static inline void tegra_dc_io_start(struct tegra_dc *dc) +{ + nvhost_module_busy(&dc->ndev->host->mod); +} + +static inline void tegra_dc_io_end(struct tegra_dc *dc) +{ + nvhost_module_idle(&dc->ndev->host->mod); +} + static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, unsigned long reg) { + BUG_ON(!nvhost_module_powered(&dc->ndev->host->mod)); return readl(dc->base + reg * 4); } static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val, unsigned long reg) { + BUG_ON(!nvhost_module_powered(&dc->ndev->host->mod)); writel(val, dc->base + reg * 4); } diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index 7e4ffa8ba0b9..1656a6e49279 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -23,13 +23,22 @@ #include #include #include +#include #include +#include +#include #include +#include