index 29577bf1f559070044ee2c9f50d300d86745f78e..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>
#define DRIVER_NAME "da8xx_lcdc"
#define LCD_V2_LIDD_CLK_EN BIT(1)
#define LCD_V2_CORE_CLK_EN BIT(0)
#define LCD_V2_LPP_B10 26
+#define LCD_V2_TFT_24BPP_MODE BIT(25)
+#define LCD_V2_TFT_24BPP_UNPACK BIT(26)
/* LCD Raster Timing 2 Register */
#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16)
unsigned int dma_end;
struct clk *lcdc_clk;
int irq;
- unsigned short pseudo_palette[16];
+ unsigned long pseudo_palette[32];
unsigned int palette_sz;
unsigned int pxl_clk;
int blank;
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 = 46666, /* 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,
},
+ /* ThreeFive S9700RTWV35TR */
+ [2] = {
+ .name = "TFC_S9700RTWV35TR_01B",
+ .width = 800,
+ .height = 480,
+ .hfp = 39,
+ .hbp = 39,
+ .hsw = 47,
+ .vfp = 13,
+ .vbp = 29,
+ .vsw = 2,
+ .pxl_clk = 30000000,
+ .invert_pxl_clk = 0,
+ },
};
/* Enable the Raster Engine of the LCD Controller */
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;
@@ -510,6 +541,13 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height,
reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8);
if (raster_order)
reg |= LCD_RASTER_ORDER;
+
+ if (bpp == 24)
+ reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE);
+ else if (bpp == 32)
+ reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE
+ | LCD_V2_TFT_24BPP_UNPACK);
+
lcdc_write(reg, LCD_RASTER_CTRL_REG);
switch (bpp) {
@@ -517,6 +555,8 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height,
case 2:
case 4:
case 16:
+ case 24:
+ case 32:
par->palette_sz = 16 * 2;
break;
par->pseudo_palette[regno] = red | green | blue;
+ if (palette[0] != 0x4000) {
+ update_hw = 1;
+ palette[0] = 0x4000;
+ }
+ } else if (((info->var.bits_per_pixel == 32) && regno < 32) ||
+ ((info->var.bits_per_pixel == 24) && regno < 24)) {
+ red >>= (24 - info->var.red.length);
+ red <<= info->var.red.offset;
+
+ green >>= (24 - info->var.green.length);
+ green <<= info->var.green.offset;
+
+ blue >>= (24 - info->var.blue.length);
+ blue <<= info->var.blue.offset;
+
+ par->pseudo_palette[regno] = red | green | blue;
+
if (palette[0] != 0x4000) {
update_hw = 1;
palette[0] = 0x4000;
@@ -636,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;
@@ -653,7 +710,9 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
if (ret < 0)
return ret;
- if (QVGA != cfg->p_disp_panel->panel_type)
+
+ if ((QVGA != cfg->p_disp_panel->panel_type) &&
+ (WVGA != cfg->p_disp_panel->panel_type))
return -EINVAL;
if (cfg->bpp <= cfg->p_disp_panel->max_bpp &&
@@ -676,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.
var->transp.offset = 0;
var->transp.length = 0;
break;
+ case 24:
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ break;
+ case 32:
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ break;
default:
err = -EINVAL;
}
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,
struct da8xx_panel *lcdc_info;
struct fb_info *da8xx_fb_info;
struct clk *fb_clk = NULL;
+ struct clk *lcdc_ick = NULL;
struct da8xx_fb_par *par;
resource_size_t len;
int ret, i;
goto err_request_mem;
}
+ /*
+ * Some SoC will not have seperate interface clock,
+ * so make lazy check here
+ */
+ lcdc_ick = clk_get(&device->dev, "lcdc_ick");
+ if (IS_ERR(lcdc_ick))
+ dev_err(&device->dev, "Can not get lcdc_ick\n");
+
+ ret = clk_enable(lcdc_ick);
+ if (ret)
+ dev_err(&device->dev, "failed to enable lcdc_ick\n");
+
fb_clk = clk_get(&device->dev, NULL);
if (IS_ERR(fb_clk)) {
dev_err(&device->dev, "Can not get device clock\n");
lcd_revision = LCD_VERSION_1;
break;
case 0x4F200800:
+ case 0x4F201000:
lcd_revision = LCD_VERSION_2;
break;
default:
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();