From 809b025947eec41bad5cbe7bd6dd46a3d262889f Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Mon, 23 Aug 2010 21:28:12 -0700 Subject: [PATCH] video: tegra: add hotplug display support This is needed for HDMI. Signed-off-by: Erik Gilling --- arch/arm/mach-tegra/include/mach/dc.h | 18 ++- arch/arm/mach-tegra/include/mach/fb.h | 11 ++ drivers/video/tegra/Kconfig | 1 + drivers/video/tegra/dc/dc.c | 202 ++++++++++++++++---------- drivers/video/tegra/dc/dc_priv.h | 36 ++++- drivers/video/tegra/dc/dc_reg.h | 54 +++++++ drivers/video/tegra/dc/rgb.c | 4 +- drivers/video/tegra/fb.c | 128 +++++++++++++--- 8 files changed, 346 insertions(+), 108 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h index 38ea7b2bf826..fc1911dfba13 100644 --- a/arch/arm/mach-tegra/include/mach/dc.h +++ b/arch/arm/mach-tegra/include/mach/dc.h @@ -57,6 +57,10 @@ enum { struct tegra_dc_out { int type; + unsigned flags; + + int dcc_bus; + int hotplug_gpio; unsigned order; unsigned align; @@ -64,11 +68,14 @@ struct tegra_dc_out { struct tegra_dc_mode *modes; int n_modes; - int (*init)(void); - int (*suspend)(pm_message_t state); - int (*resume)(void); + int (*enable)(void); + int (*disable)(void); }; +#define TEGRA_DC_OUT_HOTPLUG_HIGH (0 << 1) +#define TEGRA_DC_OUT_HOTPLUG_LOW (1 << 1) +#define TEGRA_DC_OUT_HOTPLUG_MASK (1 << 1) + #define TEGRA_DC_ALIGN_MSB 0 #define TEGRA_DC_ALIGN_LSB 1 @@ -143,6 +150,9 @@ struct tegra_dc_platform_data { struct tegra_dc *tegra_dc_get_dc(unsigned idx); 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); + /* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows * with differenct dcs in one call */ @@ -152,6 +162,6 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n); /* will probably be replaced with an interface describing the window order */ void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend); -int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode); +int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode); #endif diff --git a/arch/arm/mach-tegra/include/mach/fb.h b/arch/arm/mach-tegra/include/mach/fb.h index 58a8f41b6690..f41a04929d9e 100644 --- a/arch/arm/mach-tegra/include/mach/fb.h +++ b/arch/arm/mach-tegra/include/mach/fb.h @@ -20,12 +20,17 @@ #ifndef __MACH_TEGRA_FB_H #define __MACH_TEGRA_FB_H +#include + #ifdef CONFIG_FB_TEGRA struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, struct tegra_dc *dc, struct tegra_fb_data *fb_data, struct resource *fb_mem); void tegra_fb_unregister(struct tegra_fb_info *fb_info); +void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, + struct fb_monspecs *specs, + bool (*mode_filter)(struct fb_videomode *mode)); #else static inline struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, struct tegra_dc *dc, @@ -38,6 +43,12 @@ static inline struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev static inline void tegra_fb_unregister(struct tegra_fb_info *fb_info) { } + +void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, + struct fb_monspecs *specs, + bool (*mode_filter)(struct fb_videomode *mode)) +{ +} #endif #endif diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index 2610d137ffb8..ee3bf59d88c1 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -9,6 +9,7 @@ config TEGRA_DC tristate "Tegra Display Contoller" depends on ARCH_TEGRA select FB_MODE_HELPERS + select I2C help Tegra display controller support. diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index a28b7ed0ba46..397ade7f11d9 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -117,19 +117,6 @@ static void _dump_regs(struct tegra_dc *dc, void *data, int i; char buff[256]; - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT); - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC); DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); DUMP_REG(DC_CMD_DISPLAY_COMMAND); DUMP_REG(DC_CMD_SIGNAL_RAISE); @@ -368,12 +355,17 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) struct tegra_dc *dc; unsigned long update_mask = GENERAL_ACT_REQ; unsigned long val; - unsigned long flags; int i; dc = windows[0]->dc; - spin_lock_irqsave(&dc->lock, flags); + mutex_lock(&dc->lock); + + if (!dc->enabled) { + mutex_unlock(&dc->lock); + return -EFAULT; + } + for (i = 0; i < n; i++) { struct tegra_dc_win *win = windows[i]; unsigned h_dda; @@ -441,7 +433,8 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) tegra_dc_writel(dc, val, DC_CMD_INT_MASK); tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); - spin_unlock_irqrestore(&dc->lock, flags); + + mutex_unlock(&dc->lock); return 0; } @@ -466,6 +459,9 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n) if (n < 1 || n > DC_N_WINDOWS) return -EINVAL; + if (!windows[0]->dc->enabled) + return -EFAULT; + return wait_event_interruptible_timeout(windows[0]->dc->wq, tegra_dc_windows_are_clean(windows, n), HZ); @@ -489,7 +485,12 @@ void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend) } EXPORT_SYMBOL(tegra_dc_set_blending); -int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) +void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk) +{ + /* clock setup for DSI/HDMI to go here */ +} + +static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) { unsigned long val; unsigned long rate; @@ -547,6 +548,14 @@ int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) return 0; } + + +int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode) +{ + memcpy(&dc->mode, mode, sizeof(dc->mode)); + + return 0; +} EXPORT_SYMBOL(tegra_dc_set_mode); static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out) @@ -554,10 +563,7 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out) dc->out = out; if (out->n_modes > 0) - dc->mode = &dc->out->modes[0]; - else - dev_err(&dc->ndev->dev, - "No default modes specified. Leaving output disabled.\n"); + tegra_dc_set_mode(dc, &dc->out->modes[0]); switch (out->type) { case TEGRA_DC_OUT_RGB: @@ -568,6 +574,10 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out) dc->out_ops = NULL; break; } + + if (dc->out_ops && dc->out_ops->init) + dc->out_ops->init(dc); + } @@ -575,11 +585,9 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) { struct tegra_dc *dc = ptr; unsigned long status; - unsigned long flags; unsigned long val; int i; - status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); @@ -587,7 +595,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) int completed = 0; int dirty = 0; - spin_lock_irqsave(&dc->lock, flags); val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); for (i = 0; i < DC_N_WINDOWS; i++) { if (!(val & (WIN_A_ACT_REQ << i))) { @@ -604,8 +611,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE); } - spin_unlock_irqrestore(&dc->lock, flags); - if (completed) wake_up(&dc->wq); } @@ -618,7 +623,7 @@ static void tegra_dc_init(struct tegra_dc *dc) 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 if (dc->ndev->id == 1) + else tegra_dc_writel(dc, 0x0000011b, DC_CMD_CONT_SYNCPT_VSYNC); tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE); tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY); @@ -628,12 +633,67 @@ static void tegra_dc_init(struct tegra_dc *dc) tegra_dc_writel(dc, 0x0001c702, DC_CMD_INT_MASK); tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_ENABLE); - if (dc->mode) - tegra_dc_set_mode(dc, dc->mode); + if (dc->mode.pclk) + tegra_dc_program_mode(dc, &dc->mode); +} + +static void _tegra_dc_enable(struct tegra_dc *dc) +{ + if (dc->out && dc->out->enable) + dc->out->enable(); + tegra_dc_setup_clk(dc, dc->clk); - if (dc->out_ops && dc->out_ops->init) - dc->out_ops->init(dc); + clk_enable(dc->host1x_clk); + clk_enable(dc->clk); + tegra_periph_reset_deassert(dc->clk); + enable_irq(dc->irq); + + tegra_dc_init(dc); + + if (dc->out_ops && dc->out_ops->enable) + dc->out_ops->enable(dc); + + tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]); +} + +void tegra_dc_enable(struct tegra_dc *dc) +{ + mutex_lock(&dc->lock); + + if (!dc->enabled) { + _tegra_dc_enable(dc); + dc->enabled = true; + } + + mutex_unlock(&dc->lock); +} + +static void _tegra_dc_disable(struct tegra_dc *dc) +{ + if (dc->out_ops && dc->out_ops->disable) + dc->out_ops->disable(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(); +} + + +void tegra_dc_disable(struct tegra_dc *dc) +{ + mutex_lock(&dc->lock); + + if (dc->enabled) { + dc->enabled = false; + _tegra_dc_disable(dc); + } + + mutex_unlock(&dc->lock); } static int tegra_dc_probe(struct nvhost_device *ndev) @@ -688,9 +748,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev) goto err_release_resource_reg; } - res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem"); - if (res) - fb_mem = request_mem_region(res->start, resource_size(res), ndev->name); + fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem"); host1x_clk = clk_get(&ndev->dev, "host1x"); if (IS_ERR_OR_NULL(host1x_clk)) { @@ -698,7 +756,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev) ret = -ENOENT; goto err_iounmap_reg; } - clk_enable(host1x_clk); clk = clk_get(&ndev->dev, NULL); if (IS_ERR_OR_NULL(clk)) { @@ -707,8 +764,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev) goto err_put_host1x_clk; } - clk_enable(clk); - tegra_periph_reset_deassert(clk); dc->clk = clk; dc->host1x_clk = host1x_clk; @@ -717,9 +772,12 @@ static int tegra_dc_probe(struct nvhost_device *ndev) dc->irq = irq; dc->ndev = ndev; dc->pdata = ndev->dev.platform_data; - spin_lock_init(&dc->lock); - init_waitqueue_head(&dc->wq); + if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) + dc->enabled = true; + + mutex_init(&dc->lock); + init_waitqueue_head(&dc->wq); dc->n_windows = DC_N_WINDOWS; for (i = 0; i < dc->n_windows; i++) { @@ -734,46 +792,45 @@ static int tegra_dc_probe(struct nvhost_device *ndev) goto err_put_clk; } + /* hack to ballence enable_irq calls in _tegra_dc_enable() */ + disable_irq(dc->irq); + ret = tegra_dc_add(dc, ndev->id); if (ret < 0) { dev_err(&ndev->dev, "can't add dc\n"); goto err_free_irq; } - if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) { - if (dc->pdata->default_out) - tegra_dc_set_out(dc, dc->pdata->default_out); - else - dev_err(&ndev->dev, "No default output specified. Leaving output disabled.\n"); - } - - tegra_dc_init(dc); - if (dc->out && dc->out->init) - dc->out->init(); + nvhost_set_drvdata(ndev, dc); - tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]); + if (dc->pdata->default_out) + tegra_dc_set_out(dc, dc->pdata->default_out); + else + dev_err(&ndev->dev, "No default output specified. Leaving output disabled.\n"); - nvhost_set_drvdata(ndev, dc); + if (dc->enabled) + _tegra_dc_enable(dc); tegra_dc_dbg_add(dc); dev_info(&ndev->dev, "probed\n"); - if (fb_mem && dc->pdata->fb) { + if (dc->pdata->fb) { dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem); if (IS_ERR_OR_NULL(dc->fb)) dc->fb = NULL; } + if (dc->out_ops && dc->out_ops->detect) + dc->out_ops->detect(dc); + return 0; err_free_irq: free_irq(irq, dc); err_put_clk: - clk_disable(clk); clk_put(clk); err_put_host1x_clk: - clk_disable(host1x_clk); clk_put(host1x_clk); err_iounmap_reg: iounmap(base); @@ -793,14 +850,16 @@ static int tegra_dc_remove(struct nvhost_device *ndev) if (dc->fb) { tegra_fb_unregister(dc->fb); - release_resource(dc->fb_mem); + if (dc->fb_mem) + release_resource(dc->fb_mem); } + + if (dc->enabled) + _tegra_dc_disable(dc); + free_irq(dc->irq, dc); - tegra_periph_reset_assert(dc->clk); - clk_disable(dc->clk); clk_put(dc->clk); - clk_disable(dc->host1x_clk); clk_put(dc->host1x_clk); iounmap(dc->base); if (dc->fb_mem) @@ -816,12 +875,8 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state) dev_info(&ndev->dev, "suspend\n"); - if (dc->out && dc->out->suspend) - dc->out->suspend(state); - - disable_irq(dc->irq); - tegra_periph_reset_assert(dc->clk); - clk_disable(dc->clk); + if (dc->enabled) + _tegra_dc_disable(dc); return 0; } @@ -834,19 +889,14 @@ static int tegra_dc_resume(struct nvhost_device *ndev) dev_info(&ndev->dev, "resume\n"); - clk_enable(dc->clk); - tegra_periph_reset_deassert(dc->clk); - enable_irq(dc->irq); - - for (i = 0; i < dc->n_windows; i++) - wins[i] = &dc->windows[i]; + if (dc->enabled) { + for (i = 0; i < dc->n_windows; i++) + wins[i] = &dc->windows[i]; - tegra_dc_init(dc); - if (dc->out && dc->out->resume) - dc->out->resume(); + _tegra_dc_enable(dc); - tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]); - tegra_dc_update_windows(wins, dc->n_windows); + tegra_dc_update_windows(wins, dc->n_windows); + } return 0; } @@ -900,4 +950,4 @@ static void __exit tegra_dc_module_exit(void) } module_exit(tegra_dc_module_exit); -module_init(tegra_dc_module_init); +late_initcall(tegra_dc_module_init); diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index a2e961e7e6b7..1bc1c770ec1f 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -18,14 +18,24 @@ #ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H #define __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H -#include -#include #include +#include +#include +#include struct tegra_dc; struct tegra_dc_out_ops { - void (*init)(struct tegra_dc *dc); + /* initialize output. dc clocks are not on at this point */ + int (*init)(struct tegra_dc *dc); + /* destroy output. dc clocks are not on at this point */ + void (*destroy)(struct tegra_dc *dc); + /* detect connected display. can sleep.*/ + bool (*detect)(struct tegra_dc *dc); + /* enable output. dc clocks are on at this point */ + void (*enable)(struct tegra_dc *dc); + /* disable output. dc clocks are on at this point */ + void (*disable)(struct tegra_dc *dc); }; struct tegra_dc { @@ -41,17 +51,20 @@ struct tegra_dc { struct clk *clk; struct clk *host1x_clk; + bool enabled; + struct tegra_dc_out *out; struct tegra_dc_out_ops *out_ops; + void *out_data; - struct tegra_dc_mode *mode; + struct tegra_dc_mode mode; struct tegra_dc_win windows[DC_N_WINDOWS]; int n_windows; wait_queue_head_t wq; - spinlock_t lock; + struct mutex lock; struct resource *fb_mem; struct tegra_fb_info *fb; @@ -81,6 +94,19 @@ static inline void _tegra_dc_write_table(struct tegra_dc *dc, const u32 *table, #define tegra_dc_write_table(dc, table) \ _tegra_dc_write_table(dc, table, ARRAY_SIZE(table) / 2) +static inline void tegra_dc_set_outdata(struct tegra_dc *dc, void *data) +{ + dc->out_data = data; +} + +static inline void *tegra_dc_get_outdata(struct tegra_dc *dc) +{ + return dc->out_data; +} + +void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk); + extern struct tegra_dc_out_ops tegra_dc_rgb_ops; +extern struct tegra_dc_out_ops tegra_dc_hdmi_ops; #endif diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h index 6d6b3ba006a3..f7342cc6eb0a 100644 --- a/drivers/video/tegra/dc/dc_reg.h +++ b/drivers/video/tegra/dc/dc_reg.h @@ -136,11 +136,31 @@ #define DC_COM_CRC_CHECKSUM_LATCHED 0x329 #define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400 +#define H_PULSE_0_ENABLE (1 << 8) +#define H_PULSE_1_ENABLE (1 << 10) +#define H_PULSE_2_ENABLE (1 << 12) +#define V_PULSE_0_ENABLE (1 << 16) +#define V_PULSE_1_ENABLE (1 << 18) +#define V_PULSE_2_ENABLE (1 << 19) +#define V_PULSE_3_ENABLE (1 << 20) +#define M0_ENABLE (1 << 24) +#define M1_ENABLE (1 << 26) + #define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401 +#define DI_ENABLE (1 << 16) +#define PP_ENABLE (1 << 18) + #define DC_DISP_DISP_WIN_OPTIONS 0x402 +#define CURSOR_ENABLE (1 << 16) +#define TVO_ENABLE (1 << 28) +#define DSI_ENABLE (1 << 29) +#define HDMI_ENABLE (1 << 30) + #define DC_DISP_MEM_HIGH_PRIORITY 0x403 #define DC_DISP_MEM_HIGH_PRIORITY_TIMER 0x404 #define DC_DISP_DISP_TIMING_OPTIONS 0x405 +#define VSYNC_H_POSITION(x) ((x) & 0xfff) + #define DC_DISP_REF_TO_SYNC 0x406 #define DC_DISP_SYNC_WIDTH 0x407 #define DC_DISP_BACK_PORCH 0x408 @@ -181,6 +201,26 @@ #define DC_DISP_PP_SELECT_B 0x42b #define DC_DISP_PP_SELECT_C 0x42c #define DC_DISP_PP_SELECT_D 0x42d + +#define PULSE_MODE_NORMAL (0 << 3) +#define PULSE_MODE_ONE_CLOCK (1 << 3) +#define PULSE_POLARITY_HIGH (0 << 4) +#define PULSE_POLARITY_LOW (1 << 4) +#define PULSE_QUAL_ALWAYS (0 << 6) +#define PULSE_QUAL_VACTIVE (2 << 6) +#define PULSE_QUAL_VACTIVE1 (3 << 6) +#define PULSE_LAST_START_A (0 << 8) +#define PULSE_LAST_END_A (1 << 8) +#define PULSE_LAST_START_B (2 << 8) +#define PULSE_LAST_END_B (3 << 8) +#define PULSE_LAST_START_C (4 << 8) +#define PULSE_LAST_END_C (5 << 8) +#define PULSE_LAST_START_D (6 << 8) +#define PULSE_LAST_END_D (7 << 8) + +#define PULSE_START(x) ((x) & 0xfff) +#define PULSE_END(x) (((x) & 0xfff) << 16) + #define DC_DISP_DISP_CLOCK_CONTROL 0x42e #define PIXEL_CLK_DIVIDER_PCD1 (0 << 8) #define PIXEL_CLK_DIVIDER_PCD1H (1 << 8) @@ -213,6 +253,20 @@ #define DISP_DATA_ORDER_BLUE_RED (1 << 9) #define DC_DISP_DISP_COLOR_CONTROL 0x430 +#define BASE_COLOR_SIZE666 (0 << 0) +#define BASE_COLOR_SIZE111 (1 << 0) +#define BASE_COLOR_SIZE222 (2 << 0) +#define BASE_COLOR_SIZE333 (3 << 0) +#define BASE_COLOR_SIZE444 (4 << 0) +#define BASE_COLOR_SIZE555 (5 << 0) +#define BASE_COLOR_SIZE565 (6 << 0) +#define BASE_COLOR_SIZE332 (7 << 0) +#define BASE_COLOR_SIZE888 (8 << 0) + +#define DITHER_CONTROL_DISABLE (0 << 8) +#define DITHER_CONTROL_ORDERED (2 << 8) +#define DITHER_CONTROL_ERRDIFF (3 << 8) + #define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431 #define DC_DISP_DATA_ENABLE_OPTIONS 0x432 #define DE_SELECT_ACTIVE_BLANK 0x0 diff --git a/drivers/video/tegra/dc/rgb.c b/drivers/video/tegra/dc/rgb.c index de1a8fabd25e..c56e1f9e126b 100644 --- a/drivers/video/tegra/dc/rgb.c +++ b/drivers/video/tegra/dc/rgb.c @@ -46,7 +46,7 @@ static const u32 tegra_dc_rgb_pintable[] = { }; -void tegra_dc_rgb_init(struct tegra_dc *dc) +void tegra_dc_rgb_enable(struct tegra_dc *dc) { tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, @@ -58,6 +58,6 @@ void tegra_dc_rgb_init(struct tegra_dc *dc) } struct tegra_dc_out_ops tegra_dc_rgb_ops = { - .init = tegra_dc_rgb_init, + .enable = tegra_dc_rgb_enable, }; diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index c50c0565032e..95c7c489b513 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -35,6 +35,7 @@ struct tegra_fb_info { struct tegra_dc_win *win; struct nvhost_device *ndev; struct fb_info *info; + bool valid; struct resource *fb_mem; @@ -69,12 +70,13 @@ static int tegra_fb_release(struct fb_info *info, int user) static int tegra_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - if ((var->xres != info->var.xres) || - (var->yres != info->var.yres) || - (var->xres_virtual != info->var.xres_virtual) || - (var->yres_virtual != info->var.yres_virtual) || - (var->grayscale != info->var.grayscale)) + if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) > + info->screen_size) return -EINVAL; + + /* double yres_virtual to allow double buffering through pan_display */ + var->yres_virtual = var->yres * 2; + return 0; } @@ -104,13 +106,39 @@ static int tegra_fb_set_par(struct fb_info *info) var->blue.length = 5; tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5; break; + + case 0: + break; + default: return -EINVAL; } info->fix.line_length = var->xres * var->bits_per_pixel / 8; - tegra_dc_update_windows(&tegra_fb->win, 1); + if (var->pixclock) { + struct tegra_dc_mode mode; + info->mode = (struct fb_videomode *) + fb_find_best_mode(var, &info->modelist); + if (!info->mode) { + dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n"); + return -EINVAL; + } + + mode.pclk = PICOS2KHZ(info->mode->pixclock) * 1000; + mode.h_ref_to_sync = 1; + mode.v_ref_to_sync = 1; + mode.h_sync_width = info->mode->hsync_len; + mode.v_sync_width = info->mode->vsync_len; + mode.h_back_porch = info->mode->left_margin; + mode.v_back_porch = info->mode->upper_margin; + mode.h_active = info->mode->xres; + mode.v_active = info->mode->yres; + mode.h_front_porch = info->mode->right_margin; + mode.v_front_porch = info->mode->lower_margin; + + tegra_dc_set_mode(tegra_fb->win->dc, &mode); + } return 0; } @@ -136,6 +164,26 @@ static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green, return 0; } +static int tegra_fb_blank(int blank, struct fb_info *info) +{ + struct tegra_fb_info *tegra_fb = info->par; + + switch (blank) { + case FB_BLANK_UNBLANK: + dev_dbg(&tegra_fb->ndev->dev, "unblank\n"); + tegra_dc_enable(tegra_fb->win->dc); + return 0; + + case FB_BLANK_POWERDOWN: + dev_dbg(&tegra_fb->ndev->dev, "blank\n"); + tegra_dc_disable(tegra_fb->win->dc); + return 0; + + default: + return -ENOTTY; + } +} + static int tegra_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -187,12 +235,47 @@ static struct fb_ops tegra_fb_ops = { .fb_check_var = tegra_fb_check_var, .fb_set_par = tegra_fb_set_par, .fb_setcolreg = tegra_fb_setcolreg, + .fb_blank = tegra_fb_blank, .fb_pan_display = tegra_fb_pan_display, .fb_fillrect = tegra_fb_fillrect, .fb_copyarea = tegra_fb_copyarea, .fb_imageblit = tegra_fb_imageblit, }; +void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, + struct fb_monspecs *specs, + bool (*mode_filter)(struct fb_videomode *mode)) +{ + struct fb_event event; + int i; + + mutex_lock(&fb_info->info->lock); + fb_destroy_modedb(fb_info->info->monspecs.modedb); + + memcpy(&fb_info->info->monspecs, specs, + sizeof(fb_info->info->monspecs)); + + fb_destroy_modelist(&fb_info->info->modelist); + + for (i = 0; i < specs->modedb_len; i++) { + if (mode_filter) { + if (mode_filter(&specs->modedb[i])) + fb_add_videomode(&specs->modedb[i], + &fb_info->info->modelist); + } else { + fb_add_videomode(&specs->modedb[i], + &fb_info->info->modelist); + } + } + + fb_info->info->mode = (struct fb_videomode *) + fb_find_best_display(specs, &fb_info->info->modelist); + + event.info = fb_info->info; + fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); + mutex_unlock(&fb_info->info->lock); +} + struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, struct tegra_dc *dc, struct tegra_fb_data *fb_data, @@ -201,9 +284,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, struct tegra_dc_win *win; struct fb_info *info; struct tegra_fb_info *tegra_fb; - void __iomem *fb_base; - unsigned long fb_size; - unsigned long fb_phys; + void __iomem *fb_base = NULL; + unsigned long fb_size = 0; unsigned long fb_phys = 0; int ret = 0; win = tegra_dc_get_window(dc, fb_data->win); @@ -219,15 +301,6 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, goto err; } - fb_size = resource_size(fb_mem); - fb_phys = fb_mem->start; - fb_base = ioremap_nocache(fb_phys, fb_size); - if (!fb_base) { - dev_err(&ndev->dev, "fb can't be mapped\n"); - ret = -EBUSY; - goto err_free; - } - tegra_fb = info->par; tegra_fb->win = win; tegra_fb->ndev = ndev; @@ -236,6 +309,18 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, tegra_fb->yres = fb_data->yres; atomic_set(&tegra_fb->in_use, 0); + if (fb_mem) { + fb_size = resource_size(fb_mem); + fb_phys = fb_mem->start; + fb_base = ioremap_nocache(fb_phys, fb_size); + if (!fb_base) { + dev_err(&ndev->dev, "fb can't be mapped\n"); + ret = -EBUSY; + goto err_free; + } + tegra_fb->valid = true; + } + info->fbops = &tegra_fb_ops; info->pseudo_palette = pseudo_palette; info->screen_base = fb_base; @@ -253,13 +338,13 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, info->var.xres = fb_data->xres; info->var.yres = fb_data->yres; info->var.xres_virtual = fb_data->xres; - info->var.yres_virtual = fb_data->yres*2; + info->var.yres_virtual = fb_data->yres * 2; info->var.bits_per_pixel = fb_data->bits_per_pixel; info->var.activate = FB_ACTIVATE_VBL; /* TODO: fill in the following by querying the DC */ info->var.height = -1; info->var.width = -1; - info->var.pixclock = 24500; + info->var.pixclock = 0; info->var.left_margin = 0; info->var.right_margin = 0; info->var.upper_margin = 0; @@ -279,7 +364,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, win->virt_addr = fb_base; win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND; - tegra_fb_set_par(info); + if (fb_mem) + tegra_fb_set_par(info); if (register_framebuffer(info)) { dev_err(&ndev->dev, "failed to register framebuffer\n"); -- 2.34.1