index f550811ad2d3c044d54407c89ab51bdfe9b0207b..9e37bf4b1ed7a2c31a09aae9e5d6b05104a65c2c 100644 (file)
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/console.h>
+#include <linux/spinlock.h>
#include <linux/slab.h>
#include <video/da8xx-fb.h>
#include <asm/mach-types.h>
wait_queue_head_t vsync_wait;
int vsync_flag;
int vsync_timeout;
+ spinlock_t lock_for_chan_update;
+
+ /*
+ * LCDC has 2 ping pong DMA channels, channel 0
+ * and channel 1.
+ */
+ unsigned int which_dma_channel_done;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
.activate = 0,
.height = -1,
.width = -1,
- .pixclock = 33333, /* 46us - AUO display */
+ .pixclock = 33333,/*Pico Sec*/
.accel_flags = 0,
.left_margin = LEFT_MARGIN,
.right_margin = RIGHT_MARGIN,
unsigned char invert_pxl_clk; /* Invert Pixel clock */
};
+static vsync_callback_t vsync_cb_handler;
+static void *vsync_cb_arg;
+
static struct da8xx_panel known_lcd_panels[] = {
/* Sharp LCD035Q3DG01 */
[0] = {
.hfp = 2,
.hbp = 2,
.hsw = 41,
- .vfp = 2,
- .vbp = 2,
+ .vfp = 3,
+ .vbp = 3,
.vsw = 10,
.pxl_clk = 7833600,
.invert_pxl_clk = 0,
lcd_enable_raster();
}
-/* Configure the Burst Size of DMA */
-static int lcd_cfg_dma(int burst_size)
+/* Configure the Burst Size and fifo threhold of DMA */
+static int lcd_cfg_dma(int burst_size, int fifo_th)
{
u32 reg;
default:
return -EINVAL;
}
+
+ reg |= (fifo_th << 8);
+
lcdc_write(reg, LCD_DMA_CTRL_REG);
return 0;
@@ -679,8 +693,8 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) &
~LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG);
- /* Configure the DMA burst size. */
- ret = lcd_cfg_dma(cfg->dma_burst_sz);
+ /* Configure the DMA burst size and fifo threshold. */
+ ret = lcd_cfg_dma(cfg->dma_burst_sz, cfg->fifo_th);
if (ret < 0)
return ret;
@@ -721,6 +735,32 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
return 0;
}
+int register_vsync_cb(vsync_callback_t handler, void *arg, int idx)
+{
+ if ((vsync_cb_handler == NULL) && (vsync_cb_arg == NULL)) {
+ vsync_cb_handler = handler;
+ vsync_cb_arg = arg;
+ } else {
+ return -EEXIST;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(register_vsync_cb);
+
+int unregister_vsync_cb(vsync_callback_t handler, void *arg, int idx)
+{
+ if ((vsync_cb_handler == handler) && (vsync_cb_arg == arg)) {
+ vsync_cb_handler = NULL;
+ vsync_cb_arg = NULL;
+ } else {
+ return -ENXIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(unregister_vsync_cb);
+
/* IRQ handler for version 2 of LCDC */
static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
{
u32 reg_int;
if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
+ printk(KERN_ERR "LCDC sync lost or underflow error occured\n");
lcd_disable_raster();
+ clk_disable(par->lcdc_clk);
lcdc_write(stat, LCD_MASKED_STAT_REG);
lcd_enable_raster();
+ clk_enable(par->lcdc_clk);
} else if (stat & LCD_PL_LOAD_DONE) {
/*
* Must disable raster before changing state of any control bit.
lcdc_write(stat, LCD_MASKED_STAT_REG);
if (stat & LCD_END_OF_FRAME0) {
+ par->which_dma_channel_done = 0;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end,
LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
par->vsync_flag = 1;
wake_up_interruptible(&par->vsync_wait);
+ if (vsync_cb_handler)
+ vsync_cb_handler(vsync_cb_arg);
}
if (stat & LCD_END_OF_FRAME1) {
+ par->which_dma_channel_done = 1;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end,
LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
par->vsync_flag = 1;
wake_up_interruptible(&par->vsync_wait);
+ if (vsync_cb_handler)
+ vsync_cb_handler(vsync_cb_arg);
}
}
u32 reg_ras;
if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
+ printk(KERN_ERR "LCDC sync lost or underflow error occured\n");
lcd_disable_raster();
+ clk_disable(par->lcdc_clk);
lcdc_write(stat, LCD_STAT_REG);
lcd_enable_raster();
+ clk_enable(par->lcdc_clk);
} else if (stat & LCD_PL_LOAD_DONE) {
/*
* Must disable raster before changing state of any control bit.
if (ret == 0)
return -ETIMEDOUT;
+ if (par->panel_power_ctrl) {
+ /* Switch off panel power and backlight */
+ par->panel_power_ctrl(0);
+
+ /* Switch on panel power and backlight */
+ par->panel_power_ctrl(1);
+ }
+
return 0;
}
struct fb_fix_screeninfo *fix = &fbi->fix;
unsigned int end;
unsigned int start;
+ unsigned long irq_flags;
if (var->xoffset != fbi->var.xoffset ||
var->yoffset != fbi->var.yoffset) {
end = start + fbi->var.yres * fix->line_length - 1;
par->dma_start = start;
par->dma_end = end;
+ spin_lock_irqsave(&par->lock_for_chan_update,
+ irq_flags);
+ if (par->which_dma_channel_done == 0) {
+ lcdc_write(par->dma_start,
+ LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
+ lcdc_write(par->dma_end,
+ LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
+ } else if (par->which_dma_channel_done == 1) {
+ lcdc_write(par->dma_start,
+ LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
+ lcdc_write(par->dma_end,
+ LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
+ }
+ spin_unlock_irqrestore(&par->lock_for_chan_update,
+ irq_flags);
}
}
return ret;
}
+static int alloc_fbmem(struct da8xx_fb_par *par)
+{
+ unsigned int order, size;
+ unsigned long addr;
+
+ size = PAGE_ALIGN(par->vram_size);
+ order = get_order(size);
+ par->vram_virt = (void*) __get_free_pages(GFP_KERNEL, order);
+ addr = (unsigned long) par->vram_virt;
+ if (addr) {
+ while (size > 0) {
+ SetPageReserved(virt_to_page(addr));
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ par->vram_phys = (u32) virt_to_phys((void *) par->vram_virt);
+ return 0;
+ }
+ return -ENOMEM;
+}
+
static struct fb_ops da8xx_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = fb_check_var,
par->vram_size = PAGE_ALIGN(par->vram_size/8);
par->vram_size = par->vram_size * LCD_NUM_BUFFERS;
- par->vram_virt = dma_alloc_coherent(NULL,
- par->vram_size,
- (resource_size_t *) &par->vram_phys,
- GFP_KERNEL | GFP_DMA);
- if (!par->vram_virt) {
+ ret = alloc_fbmem(par);
+ if (ret) {
dev_err(&device->dev,
"GLCD: kmalloc for frame buffer failed\n");
ret = -EINVAL;
da8xx_fb_var.hsync_len = lcdc_info->hsw;
da8xx_fb_var.vsync_len = lcdc_info->vsw;
+ da8xx_fb_var.right_margin = lcdc_info->hfp;
+ da8xx_fb_var.left_margin = lcdc_info->hbp;
+ da8xx_fb_var.lower_margin = lcdc_info->vfp;
+ da8xx_fb_var.upper_margin = lcdc_info->vbp;
+
/* Initialize fbinfo */
da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT;
da8xx_fb_info->fix = da8xx_fb_fix;
/* initialize the vsync wait queue */
init_waitqueue_head(&par->vsync_wait);
par->vsync_timeout = HZ / 5;
+ par->which_dma_channel_done = -1;
+ spin_lock_init(&par->lock_for_chan_update);
/* Register the Frame Buffer */
if (register_framebuffer(da8xx_fb_info) < 0) {
irq_freq:
#ifdef CONFIG_CPU_FREQ
lcd_da8xx_cpufreq_deregister(par);
-#endif
err_cpu_freq:
+#endif
unregister_framebuffer(da8xx_fb_info);
err_dealloc_cmap:
{
struct fb_info *info = platform_get_drvdata(dev);
struct da8xx_fb_par *par = info->par;
+ unsigned long timeo = jiffies + msecs_to_jiffies(5000);
+ u32 stat;
console_lock();
if (par->panel_power_ctrl)
fb_set_suspend(info, 1);
lcd_disable_raster();
+
+ /* Wait for the current frame to complete */
+ do {
+ if (lcd_revision == LCD_VERSION_1)
+ stat = lcdc_read(LCD_STAT_REG);
+ else
+ stat = lcdc_read(LCD_MASKED_STAT_REG);
+ cpu_relax();
+ } while (!(stat & BIT(0)) && time_before(jiffies, timeo));
+
+ if (lcd_revision == LCD_VERSION_1)
+ lcdc_write(stat, LCD_STAT_REG);
+ else
+ lcdc_write(stat, LCD_MASKED_STAT_REG);
+
+ if (time_after_eq(jiffies, timeo)) {
+ dev_err(&dev->dev, "controller timed out\n");
+ return -ETIMEDOUT;
+ }
+
clk_disable(par->lcdc_clk);
console_unlock();
par->panel_power_ctrl(1);
clk_enable(par->lcdc_clk);
+
lcd_enable_raster();
+
+ if (par->panel_power_ctrl)
+ par->panel_power_ctrl(1);
+
fb_set_suspend(info, 0);
console_unlock();