mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-11 05:17:10 +09:00
video: tegra: add hotplug display support
This is needed for HDMI. Signed-off-by: Erik Gilling <konkers@android.com>
This commit is contained in:
committed by
Colin Cross
parent
5efab6d697
commit
809b025947
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user