video: tegra: add hotplug display support

This is needed for HDMI.

Signed-off-by: Erik Gilling <konkers@android.com>
This commit is contained in:
Erik Gilling
2010-08-23 21:28:12 -07:00
committed by Colin Cross
parent 5efab6d697
commit 809b025947
8 changed files with 347 additions and 109 deletions

View File

@@ -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

View File

@@ -20,12 +20,17 @@
#ifndef __MACH_TEGRA_FB_H
#define __MACH_TEGRA_FB_H
#include <linux/fb.h>
#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

View File

@@ -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.

View File

@@ -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);
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();
}
if (dc->out_ops && dc->out_ops->init)
dc->out_ops->init(dc);
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();
tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
nvhost_set_drvdata(ndev, dc);
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");
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);
if (dc->enabled) {
for (i = 0; i < dc->n_windows; i++)
wins[i] = &dc->windows[i];
for (i = 0; i < dc->n_windows; i++)
wins[i] = &dc->windows[i];
_tegra_dc_enable(dc);
tegra_dc_init(dc);
if (dc->out && dc->out->resume)
dc->out->resume();
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);

View File

@@ -18,14 +18,24 @@
#ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
#define __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/wait.h>
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

View File

@@ -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

View File

@@ -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,
};

View File

@@ -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");