This is needed for HDMI.
Signed-off-by: Erik Gilling <konkers@android.com>
struct tegra_dc_out {
int type;
+ unsigned flags;
+
+ int dcc_bus;
+ int hotplug_gpio;
unsigned order;
unsigned align;
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
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
*/
/* 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
#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,
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
tristate "Tegra Display Contoller"
depends on ARCH_TEGRA
select FB_MODE_HELPERS
+ select I2C
help
Tegra display controller support.
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);
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;
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;
}
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);
}
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;
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)
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:
dc->out_ops = NULL;
break;
}
+
+ if (dc->out_ops && dc->out_ops->init)
+ dc->out_ops->init(dc);
+
}
{
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);
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))) {
tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
}
- spin_unlock_irqrestore(&dc->lock, flags);
-
if (completed)
wake_up(&dc->wq);
}
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);
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)
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)) {
ret = -ENOENT;
goto err_iounmap_reg;
}
- clk_enable(host1x_clk);
clk = clk_get(&ndev->dev, NULL);
if (IS_ERR_OR_NULL(clk)) {
goto err_put_host1x_clk;
}
- clk_enable(clk);
- tegra_periph_reset_deassert(clk);
dc->clk = clk;
dc->host1x_clk = host1x_clk;
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++) {
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);
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)
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;
}
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;
}
}
module_exit(tegra_dc_module_exit);
-module_init(tegra_dc_module_init);
+late_initcall(tegra_dc_module_init);
#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 {
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;
#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
#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
#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)
#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
};
-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,
}
struct tegra_dc_out_ops tegra_dc_rgb_ops = {
- .init = tegra_dc_rgb_init,
+ .enable = tegra_dc_rgb_enable,
};
struct tegra_dc_win *win;
struct nvhost_device *ndev;
struct fb_info *info;
+ bool valid;
struct resource *fb_mem;
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;
}
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;
}
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)
{
.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,
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);
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;
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;
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;
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");