ckfwq/linux-3.0.4/drivers/video/gsc3280_lcdfb.c

653 lines
16 KiB
C

/*
* GSC3280 SoC LCD framebuffer driver
*
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
* Author: Davied, apple_guet@126.com
*
*/
#include <linux/fb.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeup.h>
#include <linux/dma-mapping.h>
#include <video/gsc3280_lcdfb.h>
#include <linux/platform_device.h>
#ifdef GSC3280_VIDEO_DEBUG
#define DBG(msg...) do { \
printk(KERN_INFO msg); \
} while (0)
#else
#define DBG(msg...) do { } while(0)
#endif
typedef union {
/* raw register data */
u32 w32;
/* register bits */
struct {
unsigned reserved:1;
unsigned cd:1; /* 0:16bpp, 1:8bpp */
unsigned hsl:1; /* 0:high, 1:low */
unsigned vsl:1; /* 0:high, 1:low */
unsigned vml:1; /* 0:high, 1:low */
unsigned reseved:1;
unsigned hce:1; /* hard cursor, 0:disable, 1:enable */
unsigned reserved7_31:25;
} b;
} lcd_ctrl_u;
typedef union {
/* raw register data */
u32 w32;
/* register bits */
struct {
unsigned thgate0_15:16;
unsigned thgdel16_23:8;
unsigned thsync24_31:8;
} b;
} lcd_htim_u;
typedef union {
/* raw register data */
u32 w32;
/* register bits */
struct {
unsigned tvgate0_15:16;
unsigned tvgdel16_23:8;
unsigned tvsync24_31:8;
} b;
} lcd_vtim_u;
typedef union {
/* raw register data */
u32 w32;
/* register bits */
struct {
unsigned tvlen0_15:16;
unsigned thlen16_31:16;
} b;
} lcd_hvlen_u;
typedef union {
/* raw register data */
u32 w32;
/* register bits */
struct {
unsigned ven0:1; /* 0:close, 1:start */
unsigned reserved1_31:31;
} b;
} lcd_cen_u;
struct gsc3280_fb {
unsigned is_enabled:1;
unsigned suspend_flg:1;
lcd_ctrl_u ctrl;
lcd_htim_u htim;
lcd_vtim_u vtim;
lcd_hvlen_u hvlen;
struct clk *clk;
void *vidmem;
spinlock_t slock;
struct fb_info *fb;
struct device *dev;
size_t vidmem_size;
void __iomem *base;
struct resource *mem;
dma_addr_t dma_addr;
struct fb_cursor *cursor;
uint32_t __iomem *pseudo_palette;
struct gsc3280_fb_platform_data *pdata;
};
static const struct fb_fix_screeninfo gsc3280fb_fix __devinitdata = {
.id = "GSC3280FB",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.accel = FB_ACCEL_NONE,
};
/* set or change the hardware cursor parameters */
static inline int gsc3280fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
lcd_ctrl_u ctrl;
struct gsc3280_fb *gsc = info->par;
unsigned long bg = 0, fg = 0, hwc_addr = 0;
dev_dbg(NULL, "%s(%p,%p)\n", __func__, info, cursor);
/* check not being asked to exceed capabilities */
if (cursor->image.width > 64)
return -EINVAL;
if (cursor->image.height > 64)
return -EINVAL;
if (cursor->image.depth > 16)
return -EINVAL;
ctrl.w32= readl(gsc->base + GSC3280_REG_LCD_CTRL);
hwc_addr = readl(gsc->base + GSC3280_REG_LCD_CBAR);
if (cursor->enable) {
ctrl.b.hce = 1;
writel(ctrl.w32, gsc->base + GSC3280_REG_LCD_CTRL);
//writel(fbi->cursor.k_addr, base + GSC3280_REG_LCD_FB_CABR);
}
else {
ctrl.b.hce = 0;
writel(ctrl.w32, gsc->base + GSC3280_REG_LCD_CTRL);
}
/* set data */
if (cursor->set & FB_CUR_SETPOS) {
unsigned int x = cursor->image.dx;
unsigned int y = cursor->image.dy;
if ((x >= 2048) || (y >= 2048))
return -EINVAL;
//y += cursor->image.height;
writel(x | (y << 16), gsc->base + GSC3280_REG_LCD_XYPOS);
}
if (cursor->set & FB_CUR_SETCMAP) {
unsigned int bg_col = cursor->image.bg_color;
unsigned int fg_col = cursor->image.fg_color;
bg = ((info->cmap.red[bg_col] & 0xF8) << 8) | ((info->cmap.green[bg_col] & 0xFC) << 3)
| ((info->cmap.blue[bg_col] & 0xF8) >> 3);
fg = ((info->cmap.red[fg_col] & 0xF8) << 8) | ((info->cmap.green[fg_col] & 0xFC) << 3)
| ((info->cmap.blue[fg_col] & 0xF8) >> 3);
memset((char *)hwc_addr, bg, cursor->image.dx * cursor->image.dy);
//memset(hwc_addr, fg, cursor->image.dx * cursor->image.dy);
}
return 0;
}
static int gsc3280fb_get_controller_bpp(struct gsc3280_fb *gsc)
{
switch (gsc->pdata->bpp) {
case 8:
return 8;
case 15:
case 16:
return 16;
default:
return gsc->pdata->bpp;
}
}
static struct fb_videomode *gsc3280fb_get_mode(struct gsc3280_fb *gsc, struct fb_var_screeninfo *var)
{
size_t i = 0;
struct fb_videomode *mode = gsc->pdata->modes;
for (i = 0; i < gsc->pdata->num_modes && mode != NULL; ++i, ++mode) {
if ((mode->xres == var->xres) && (mode->yres == var->yres)) {
return mode;
}
}
return NULL;
}
static int gsc3280fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
{
int ret = 0;
struct fb_videomode *mode;
struct gsc3280_fb *gsc = fb->par;
if (var->bits_per_pixel != gsc3280fb_get_controller_bpp(gsc) &&
var->bits_per_pixel != gsc->pdata->bpp) {
ret = -EINVAL;
goto quit;
}
mode = gsc3280fb_get_mode(gsc, var);
if (mode == NULL) {
ret = -EINVAL;
goto quit;
}
fb_videomode_to_var(var, mode);
switch (gsc->pdata->bpp) {
case 8:
break;
case 15:
var->red.offset = 10;
var->red.length = 5;
var->green.offset = 6;
var->green.length = 5;
var->blue.offset = 0;
var->blue.length = 5;
break;
case 16:
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
break;
default:
break;
}
quit:
return ret;
}
//set lcd control register
static int gsc3280fb_set_par(struct fb_info *info)
{
int ret = 0;
struct fb_videomode *mode;
struct gsc3280_fb *gsc = info->par;
struct fb_var_screeninfo *var = &info->var;
gsc->ctrl.w32 = 0;
switch (gsc->pdata->bpp) {
case 16:
gsc->ctrl.b.cd = 0;
break;
case 8:
gsc->ctrl.b.cd = 1;
break;
default:
break;
}
if (!(gsc->pdata->modes->sync & FB_SYNC_HOR_HIGH_ACT))
gsc->ctrl.b.hsl = 1;
if (!(gsc->pdata->modes->sync & FB_SYNC_VERT_HIGH_ACT))
gsc->ctrl.b.vsl = 1;
gsc->ctrl.b.vml = 1;
writel(gsc->ctrl.w32, gsc->base + GSC3280_REG_LCD_CTRL);
mode = gsc3280fb_get_mode(gsc, var);
if (mode == NULL) {
ret = -EINVAL;
goto quit;
}
if (mode == info->mode) {
goto quit;
}
info->mode = mode;
gsc->htim.w32 = 0;
gsc->vtim.w32 = 0;
gsc->hvlen.w32 = 0;
gsc->htim.b.thgate0_15 = mode->xres - 1;
gsc->htim.b.thgdel16_23 = mode->left_margin;
gsc->htim.b.thsync24_31 = mode->hsync_len;
gsc->vtim.b.tvgate0_15 = mode->yres - 1;
gsc->vtim.b.tvgdel16_23 = mode->upper_margin;
gsc->vtim.b.tvsync24_31 = mode->vsync_len;
gsc->hvlen.b.tvlen0_15 = mode->vsync_len + mode->upper_margin + mode->lower_margin + mode->yres- 1;
gsc->hvlen.b.thlen16_31 = mode->hsync_len + mode->left_margin + mode->right_margin + mode->xres -1;
writel(gsc->htim.w32, gsc->base + GSC3280_REG_LCD_HTIM);
writel(gsc->vtim.w32, gsc->base + GSC3280_REG_LCD_VTIM);
writel(gsc->hvlen.w32, gsc->base + GSC3280_REG_LCD_HVLEN);
quit:
return 0;
}
static void gsc3280fb_enable(struct gsc3280_fb *gsc)
{
lcd_cen_u cen;
cen.w32 = 0;
cen.b.ven0 = 1;
writel(cen.w32, gsc->base + GSC3280_REG_LCD_CEN);
}
static void gsc3280fb_disable(struct gsc3280_fb *gsc)
{
lcd_cen_u cen;
u32 cnt = 1000;
cen.w32= readl(gsc->base + GSC3280_REG_LCD_CEN);
cen.b.ven0 = 0;
writel(cen.w32, gsc->base + GSC3280_REG_LCD_CEN);
do {
cen.w32 = readl(gsc->base + GSC3280_REG_LCD_CEN);
} while ((cen.b.ven0 == 1) && (cnt-- != 0));
}
static int gsc3280fb_blank(int blank_mode, struct fb_info *info)
{
unsigned long flags = 0;
struct gsc3280_fb *gsc = info->par;
spin_lock_irqsave(&gsc->slock, flags);
switch (blank_mode) {
case FB_BLANK_UNBLANK:
if (gsc->is_enabled) {
goto exit;
}
gsc3280fb_enable(gsc);
gsc->is_enabled = 1;
break;
default:
if (!gsc->is_enabled) {
goto exit;
}
gsc3280fb_disable(gsc);
gsc->is_enabled = 0;
break;
}
exit:
spin_unlock_irqrestore(&gsc->slock, flags);
return 0;
}
#if 0
static int gsc3280fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct gsc3280_fb *par = info->par;
u32 vram_addr = par->vram_addr + (var->yoffset * info->fix.line_length
+ var->xoffset * info->var.bits_per_pixel / 8) ;
DEBUG_MSG(KERN_DEBUG "viafb_pan_display, address = %d\n", vram_addr);
if (is_first_display) {
//gsc3280_set_primary_address(vram_addr);
}
else
//gsc3280_set_secondary_address(vram_addr);
return 0;
}
#endif
static struct fb_ops gsc3280fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = gsc3280fb_check_var,
.fb_set_par = gsc3280fb_set_par,
.fb_blank = gsc3280fb_blank,
//.fb_pan_display = gsc3280_pan_display,
.fb_cursor = gsc3280fb_cursor,
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
};
static int __devinit gsc3280fb_alloc_dmamem(struct gsc3280_fb *gsc)
{
u32 i = 0, max_videosize = 0;
struct fb_videomode *mode = gsc->pdata->modes;
for (i = 0; i < gsc->pdata->num_modes; mode++, i++) {
if (max_videosize < mode->xres * mode->yres)
max_videosize = mode->xres * mode->yres;
}
max_videosize *= gsc3280fb_get_controller_bpp(gsc) >> 3;
gsc->vidmem_size = PAGE_ALIGN(max_videosize);
gsc->vidmem = dma_alloc_coherent(gsc->dev, gsc->vidmem_size, &gsc->dma_addr, GFP_KERNEL);
if (!gsc->vidmem)
return -ENOMEM;
return 0;
}
static void gsc3280fb_free_devmem(struct gsc3280_fb *gsc)
{
dma_free_coherent(gsc->dev, gsc->vidmem_size, gsc->vidmem, gsc->dma_addr);
}
static int __devinit gsc3280fb_probe(struct platform_device *pdev)
{
int ret = 0;
struct fb_info *fb = NULL;
struct gsc3280_fb *gsc = NULL;
struct gsc3280_fb_platform_data *pdata = pdev->dev.platform_data;
DBG("############\n");
printk(KERN_INFO "gsc3280_lcdfb_probe start.\n");
if (!pdata) {
DBG("Missing platform data\n");
return -ENXIO;
}
#ifdef CONFIG_FB_GSC3280_SVGA
*(volatile unsigned int *)0xbc04a024 = 0x04;
#else
*(volatile unsigned int *)0xbc04a024 = 0x05;
#endif
fb = framebuffer_alloc(sizeof(struct gsc3280_fb), &pdev->dev);
if (!fb) {
DBG("Failed to allocate framebuffer device!!!!\n");
return -ENOMEM;
}
fb->flags = FBINFO_DEFAULT;
fb->fbops = &gsc3280fb_ops;
gsc = fb->par;
gsc->pdata = pdata;
gsc->dev = &pdev->dev;
gsc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!gsc->mem) {
DBG("Failed to get register memory resource!!!!\n");
ret = -ENXIO;
goto err_framebuffer_alloc;
}
gsc->mem = request_mem_region(gsc->mem->start, resource_size(gsc->mem), pdev->name);
if (!gsc->mem) {
DBG("Failed to request register memory region!!!!\n");
ret = -EBUSY;
goto err_framebuffer_alloc;
}
gsc->base = ioremap(gsc->mem->start, resource_size(gsc->mem));
if (!gsc->base) {
DBG("Failed to ioremap register memory region!!!!\n");
ret = -EBUSY;
goto err_release_mem_region;
}
DBG("gsc->base = %p\n", gsc->base);
gsc->pseudo_palette = gsc->base + GSC3280_REG_LCD_PLATE;
DBG("gsc->pseudo_palette = %p\n", gsc->pseudo_palette);
gsc->clk = clk_get(NULL, "lcd");
if (IS_ERR(gsc->clk)) {
DBG("failed to find watchdog clock source!!!!\n");
ret = -ENXIO;
goto err_iounmap;
}
DBG("lcd clk rate is %ld\n", clk_get_rate(gsc->clk));
clk_enable(gsc->clk);
platform_set_drvdata(pdev, gsc);
spin_lock_init(&gsc->slock);
fb_videomode_to_modelist(pdata->modes, pdata->num_modes, &fb->modelist);
fb_videomode_to_var(&fb->var, pdata->modes);
fb->var.bits_per_pixel = pdata->bpp;
ret = gsc3280fb_check_var(&fb->var, fb);
if (ret < 0) {
DBG("gsc3280fb_check_var error!!!!\n");
goto err_put_lpclk;
}
ret = gsc3280fb_alloc_dmamem(gsc);
if (ret) {
DBG("Failed to allocate video memory!!!!\n");
goto err_put_lpclk;
}
fb->fix = gsc3280fb_fix;
fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
fb->fix.mmio_start = gsc->mem->start;
fb->fix.mmio_len = resource_size(gsc->mem);
fb->fix.smem_start = gsc->dma_addr;
fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
fb->screen_base = gsc->vidmem;
fb->pseudo_palette = gsc->pseudo_palette;
fb_alloc_cmap(&fb->cmap, 256, 0);
gsc->is_enabled = 1;
gsc->suspend_flg = 0;
writel(gsc->dma_addr, gsc->base + GSC3280_REG_LCD_VBAR);
fb->mode = NULL;
ret = register_framebuffer(fb);
if (ret) {
DBG("Failed to register framebuffer!!!!\n");
goto err_free_devmem;
}
gsc->fb = fb;
gsc3280fb_set_par(fb);
#ifdef CONFIG_PM
/* set pm runtime power state and register with power system */
pm_runtime_set_active(gsc->dev);
pm_runtime_enable(gsc->dev);
pm_runtime_resume(gsc->dev);
//device_init_wakeup(gsc->dev, 1);
#endif
#ifdef CONFIG_FB_GSC3280_VGA
DBG("init vga mode");
my_vga_mode = 1;
writel(0xd78704ff, gsc->base + GSC3280_REG_LCD_HTIM);
writel(0x1d0203bf, gsc->base + GSC3280_REG_LCD_VTIM);
writel(0x06b003e2, gsc->base + GSC3280_REG_LCD_HVLEN);
#endif
gsc3280fb_enable(gsc);
printk(KERN_INFO "gsc3280_lcdfb_probe success\n");
DBG("############\n");
return 0;
err_free_devmem:
fb_dealloc_cmap(&fb->cmap);
gsc3280fb_free_devmem(gsc);
err_put_lpclk:
clk_put(gsc->clk);
err_iounmap:
iounmap(gsc->base);
iounmap(gsc->pseudo_palette);
err_release_mem_region:
release_mem_region(gsc->mem->start, resource_size(gsc->mem));
err_framebuffer_alloc:
framebuffer_release(fb);
printk(KERN_INFO "!!!!!!gsc3280_lcdfb_probe error!!!!!!\n");
return ret;
}
static int __devexit gsc3280fb_remove(struct platform_device *pdev)
{
struct gsc3280_fb *gsc = platform_get_drvdata(pdev);
gsc3280fb_blank(FB_BLANK_POWERDOWN, gsc->fb);
iounmap(gsc->base);
release_mem_region(gsc->mem->start, resource_size(gsc->mem));
fb_dealloc_cmap(&gsc->fb->cmap);
gsc3280fb_free_devmem(gsc);
platform_set_drvdata(pdev, NULL);
clk_put(gsc->clk);
framebuffer_release(gsc->fb);
return 0;
}
static void gsc3280fb_shutdown(struct platform_device *pdev)
{
//struct gsc3280_fb *gsc = platform_get_drvdata(pdev);
DBG("gsc3280fb_shutdown\n");
}
#ifdef CONFIG_PM
static int gsc3280fb_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gsc3280_fb *gsc = platform_get_drvdata(pdev);
printk(KERN_INFO "gsc3280 fb suspend\n");
if (gsc->suspend_flg == 1) {
#ifdef CONFIG_GSC3280_1WIRE_TS
wire1_backlight_control(0); //close backlight
#endif
gsc3280fb_disable(gsc);
clk_disable(gsc->clk);
} else {
gsc->suspend_flg = 1;
}
return 0;
}
static int gsc3280fb_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gsc3280_fb *gsc = platform_get_drvdata(pdev);
printk(KERN_INFO "gsc3280 fb resume\n");
#ifdef CONFIG_FB_GSC3280_SVGA
*(volatile unsigned int *)0xbc04a024 = 0x04;
#else
*(volatile unsigned int *)0xbc04a024 = 0x05;
#endif
clk_enable(gsc->clk);
//soft reset
*(volatile unsigned int *)0xbc04a014 = *(volatile unsigned int *)0xbc04a014 | (1 << 1);
mdelay(5);
*(volatile unsigned int *)0xbc04a014 = *(volatile unsigned int *)0xbc04a014 & (~(1 << 1));
mdelay(1);
writel(gsc->dma_addr, gsc->base + GSC3280_REG_LCD_VBAR);
writel(gsc->ctrl.w32, gsc->base + GSC3280_REG_LCD_CTRL);
writel(gsc->htim.w32, gsc->base + GSC3280_REG_LCD_HTIM);
writel(gsc->vtim.w32, gsc->base + GSC3280_REG_LCD_VTIM);
writel(gsc->hvlen.w32, gsc->base + GSC3280_REG_LCD_HVLEN);
#ifdef CONFIG_FB_GSC3280_VGA
DBG("init vga mode");
my_vga_mode = 1;
writel(0xd78704ff, gsc->base + GSC3280_REG_LCD_HTIM);
writel(0x1d0203bf, gsc->base + GSC3280_REG_LCD_VTIM);
writel(0x06b003e2, gsc->base + GSC3280_REG_LCD_HVLEN);
#endif
mdelay(2);
gsc3280fb_enable(gsc);
#ifdef CONFIG_GSC3280_1WIRE_TS
wire1_backlight_control(127); //open backlight
#endif
return 0;
}
#else
#define gsc3280fb_suspend NULL
#define gsc3280fb_resume NULL
#endif //end #ifdef CONFIG_PM
static const struct dev_pm_ops gsc3280fb_pm_ops = {
.suspend = gsc3280fb_suspend,
.resume = gsc3280fb_resume,
.runtime_suspend = gsc3280fb_suspend,
.runtime_resume = gsc3280fb_resume,
};
static struct platform_driver gsc3280fb_driver = {
.probe = gsc3280fb_probe,
.remove = __devexit_p(gsc3280fb_remove),
.shutdown = gsc3280fb_shutdown,
.driver = {
.name = "gsc3280-lcdfb",
.pm = &gsc3280fb_pm_ops,
},
};
static int __init gsc3280fb_init(void)
{
return platform_driver_register(&gsc3280fb_driver);
}
static void __exit gsc3280fb_exit(void)
{
platform_driver_unregister(&gsc3280fb_driver);
}
subsys_initcall(gsc3280fb_init);
device_exit(gsc3280fb_exit);
//device_init(gsc3280fb_init);
//device_exit(gsc3280fb_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Davie <apple_guet@126.com>");
MODULE_DESCRIPTION("GSC3280 SoC LCD framebuffer driver");
MODULE_ALIAS("platform:gsc3280-fb");