mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-01 21:17:31 +02:00
1539 lines
39 KiB
C
Executable File
1539 lines
39 KiB
C
Executable File
/*
|
|
* linux/drivers/video/jzlcd.c -- Ingenic On-Chip LCD frame buffer device
|
|
*
|
|
* Copyright (C) 2005-2007, Ingenic Semiconductor Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/fb.h>
|
|
#include <linux/init.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/kthread.h>
|
|
|
|
#include <asm/irq.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/system.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/jzsoc.h>
|
|
|
|
#include "console/fbcon.h"
|
|
|
|
#include "jzlcd.h"
|
|
|
|
#define DRIVER_NAME "jz-lcd"
|
|
|
|
#undef DEBUG
|
|
//#define DEBUG
|
|
#ifdef DEBUG
|
|
#define dprintk(x...) printk(x)
|
|
#else
|
|
#define dprintk(x...)
|
|
#endif
|
|
|
|
#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg)
|
|
#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg)
|
|
#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg)
|
|
#ifdef DEBUG
|
|
#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg)
|
|
#else
|
|
#define print_dbg(f, arg...) do {} while (0)
|
|
#endif
|
|
|
|
struct lcd_cfb_info {
|
|
struct fb_info fb;
|
|
struct display_switch *dispsw;
|
|
signed int currcon;
|
|
int func_use_count;
|
|
|
|
struct {
|
|
u16 red, green, blue;
|
|
} palette[NR_PALETTE];
|
|
#ifdef CONFIG_PM
|
|
struct pm_dev *pm;
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
struct task_struct *rotate_daemon_thread;
|
|
#endif
|
|
};
|
|
|
|
static struct lcd_cfb_info *jzlcd_info;
|
|
|
|
struct jzfb_info {
|
|
unsigned int cfg; /* panel mode and pin usage etc. */
|
|
unsigned int w;
|
|
unsigned int h;
|
|
unsigned int bpp; /* bit per pixel */
|
|
unsigned int fclk; /* frame clk */
|
|
unsigned int hsw; /* hsync width, in pclk */
|
|
unsigned int vsw; /* vsync width, in line count */
|
|
unsigned int elw; /* end of line, in pclk */
|
|
unsigned int blw; /* begin of line, in pclk */
|
|
unsigned int efw; /* end of frame, in line count */
|
|
unsigned int bfw; /* begin of frame, in line count */
|
|
};
|
|
|
|
static struct jzfb_info jzfb = {
|
|
#if defined(CONFIG_JZLCD_SHARP_LQ035Q7)
|
|
MODE_TFT_SHARP | PCLK_N | VSYNC_N,
|
|
240, 320, 16, 60, 1, 2, 1, 2, 0, 6
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_SAMSUNG_LTS350Q1)
|
|
MODE_TFT_SAMSUNG | PCLK_N,
|
|
240, 320, 16, 60, 1, 2, (254-240), 0, 7, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04)
|
|
MODE_TFT_GEN | HSYNC_N | VSYNC_N,
|
|
320, 240, 16, 70, 19, 4, 20, 14, 18, 6
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF01)
|
|
MODE_TFT_GEN | HSYNC_N | VSYNC_N,
|
|
480, 272, 16, 60, 41, 10, 2, 2, 2, 2
|
|
#endif
|
|
|
|
#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF02)
|
|
/* MODE_TFT_18BIT: JZ4740@ version */
|
|
MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N,
|
|
480, 272, 32, 60, 41, 10, 2, 2, 2, 2
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW)
|
|
MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N,
|
|
320, 240, 16, 85, 30, 3, 38, 20, 11, 8
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL)
|
|
MODE_8BIT_SERIAL_TFT | HSYNC_N | VSYNC_N | PCLK_N,
|
|
/* serial mode 280 lines, parallel mode 240 lines */
|
|
320, 280, 32, 60, (30*3), 3, (20*3), (38*3), 46, 23
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_AUO_A030FL01_V1)
|
|
MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N,
|
|
480, 272, 32, 60, 39, 10, 8, 4, 4, 2
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E)
|
|
MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N | DE_N,
|
|
320, 240, 16, 60, 3, 3, 3, 3, 3, 85 /* 320x240 */
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && defined(CONFIG_JZ4740_PAVO)
|
|
MODE_TFT_GEN | HSYNC_N | VSYNC_N | MODE_TFT_18BIT | PCLK_N,
|
|
// 320, 240, 18, 110, 1, 1, 10, 50, 10, 13
|
|
320, 240, 18, 80, 1, 1, 10, 50, 10, 13
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && !(defined(CONFIG_JZ4740_PAVO))
|
|
MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N,
|
|
320, 240, 16, 110, 1, 1, 10, 50, 10, 13
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL)
|
|
MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N,
|
|
320, 240, 32, 60, 1, 1, 10, 50, 10, 13
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_HYNIX_HT10X21)
|
|
MODE_TFT_GEN | PCLK_N,
|
|
1024, 768, 16, 45, 1, 1, 75, 0, 3, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_TOSHIBA_LTM084P363)
|
|
MODE_TFT_GEN | PCLK_N,
|
|
800, 600, 16, 50, 1, 2, 199, 0, 2, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)
|
|
MODE_TFT_SHARP | PCLK_N,
|
|
800, 600, 16, 40, 1, 1, 255, 0, 34, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_CSTN_800x600)
|
|
MODE_STN_COLOR_DUAL | STN_DAT_PIN8,
|
|
800, 600, 16, 30, 8, 1, 0, 0, 0, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_CSTN_320x240)
|
|
MODE_STN_COLOR_SINGLE | STN_DAT_PIN8,
|
|
320, 240, 16, 120, 8, 1, 8, 0, 0, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_MSTN_640x480)
|
|
MODE_STN_MONO_DUAL | STN_DAT_PIN4,
|
|
640, 480, 8, 110, 4, 1, 4, 0, 0, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_MSTN_320x240)
|
|
MODE_STN_MONO_SINGLE | STN_DAT_PIN4,
|
|
320, 240, 8, 110, 4, 1, 4, 0, 0, 0
|
|
#endif
|
|
#if defined(CONFIG_JZLCD_MSTN_480x320)
|
|
MODE_STN_MONO_SINGLE | STN_DAT_PIN8
|
|
#if defined(CONFIG_JZLCD_MSTN_INVERSE)
|
|
| DATA_INVERSE
|
|
#endif
|
|
, 480, 320, 8, 65, 8, 1, 8, 0, 0, 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_JZLCD_MSTN_240x128)
|
|
MODE_STN_MONO_SINGLE | STN_DAT_PIN1
|
|
#if defined(CONFIG_JZLCD_MSTN_INVERSE)
|
|
| DATA_INVERSE
|
|
#endif
|
|
, 240, 128, 8, 100, 1, 1, 1, 0, 0, 0
|
|
#endif
|
|
};
|
|
|
|
static struct lcd_desc *lcd_desc_base;
|
|
static struct lcd_desc *lcd_palette_desc;
|
|
static struct lcd_desc *lcd_frame_desc0;
|
|
static struct lcd_desc *lcd_frame_desc1;
|
|
|
|
static unsigned char *lcd_palette;
|
|
static unsigned char *lcd_frame[CONFIG_JZLCD_FRAMEBUFFER_MAX];
|
|
struct jz_lcd_buffer_addrs_t jz_lcd_buffer_addrs;
|
|
//extern struct display fb_display[MAX_NR_CONSOLES];
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
static unsigned char *lcd_frame_user_fb;
|
|
/* default rotate angle */
|
|
static volatile int rotate_angle = CONFIG_JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE;
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
static void print_regs(void) /* debug */
|
|
{
|
|
printk("REG_LCD_CFG:\t0x%8.8x\n", REG_LCD_CFG);
|
|
printk("REG_LCD_VSYNC:\t0x%8.8x\n", REG_LCD_VSYNC);
|
|
printk("REG_LCD_HSYNC:\t0x%8.8x\n", REG_LCD_HSYNC);
|
|
printk("REG_LCD_VAT:\t0x%8.8x\n", REG_LCD_VAT);
|
|
printk("REG_LCD_DAH:\t0x%8.8x\n", REG_LCD_DAH);
|
|
printk("REG_LCD_DAV:\t0x%8.8x\n", REG_LCD_DAV);
|
|
printk("REG_LCD_PS:\t0x%8.8x\n", REG_LCD_PS);
|
|
printk("REG_LCD_CLS:\t0x%8.8x\n", REG_LCD_CLS);
|
|
printk("REG_LCD_SPL:\t0x%8.8x\n", REG_LCD_SPL);
|
|
printk("REG_LCD_REV:\t0x%8.8x\n", REG_LCD_REV);
|
|
printk("REG_LCD_CTRL:\t0x%8.8x\n", REG_LCD_CTRL);
|
|
printk("REG_LCD_STATE:\t0x%8.8x\n", REG_LCD_STATE);
|
|
printk("REG_LCD_IID:\t0x%8.8x\n", REG_LCD_IID);
|
|
printk("REG_LCD_DA0:\t0x%8.8x\n", REG_LCD_DA0);
|
|
printk("REG_LCD_SA0:\t0x%8.8x\n", REG_LCD_SA0);
|
|
printk("REG_LCD_FID0:\t0x%8.8x\n", REG_LCD_FID0);
|
|
printk("REG_LCD_CMD0:\t0x%8.8x\n", REG_LCD_CMD0);
|
|
|
|
printk("==================================\n");
|
|
printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff);
|
|
printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff);
|
|
printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff);
|
|
printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff);
|
|
printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff);
|
|
printk("==================================\n");
|
|
|
|
}
|
|
#else
|
|
#define print_regs()
|
|
#endif
|
|
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
static int jzfb_rotate_daemon_thread(void *info)
|
|
{
|
|
int i,j;
|
|
struct fb_info *fb = &jzlcd_info->fb;
|
|
|
|
while (!kthread_should_stop()) {
|
|
#if (CONFIG_JZLCD_FRAMEBUFFER_BPP == 8)
|
|
unsigned char *plcd_frame = (unsigned char *)lcd_frame[0];
|
|
unsigned char *pfb = (unsigned char *) (fb->screen_base);
|
|
#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 16)
|
|
unsigned short *plcd_frame = (unsigned short *)lcd_frame[0];
|
|
unsigned short *pfb = (unsigned short *) (fb->screen_base);
|
|
#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 32)
|
|
unsigned int *plcd_frame = (unsigned int *)lcd_frame[0];
|
|
unsigned int *pfb = (unsigned int *) (fb->screen_base);
|
|
#else
|
|
#error "ERROR, rotate not support this bpp."
|
|
#endif
|
|
switch ( rotate_angle ) {
|
|
case FB_ROTATE_UR:
|
|
printk("%s, Warning, this shouldn't reache\n", __FUNCTION__);
|
|
ssleep(1);
|
|
break;
|
|
case FB_ROTATE_UD: /* cost about 30ms, can be accelrated by dma in the future */
|
|
plcd_frame += jzfb.w*jzfb.h -1;
|
|
for (i=0;i<jzfb.h*jzfb.w;i++)
|
|
*plcd_frame-- = *pfb++;
|
|
msleep(75);
|
|
break;
|
|
case FB_ROTATE_CW: /* cost about 80ms */
|
|
for (i=1;i<fb->var.height+1; i++) {
|
|
for (j=1; j < fb->var.width+1; j++)
|
|
plcd_frame[j*fb->var.height-i] = *pfb++;
|
|
}
|
|
msleep(100); /* sleep 100ms */
|
|
break;
|
|
case FB_ROTATE_CCW: /* cost about 80ms */
|
|
for (i=0;i<fb->var.height;i++) {
|
|
for ( j=fb->var.width-1;j>=0;j--)
|
|
plcd_frame[j*fb->var.height+i] = *pfb++;
|
|
}
|
|
msleep(100); /* sleep 100ms */
|
|
break;
|
|
default: /* FB_ROTATE_UR */
|
|
dprintk("Unknown rotate(%d) type\n", rotate_angle);
|
|
ssleep(1);
|
|
}
|
|
|
|
dma_cache_wback_inv((unsigned int)(lcd_frame_user_fb), fb->fix.smem_len);
|
|
}
|
|
return 0;
|
|
}
|
|
/*
|
|
* rotate param angle:
|
|
* 0: FB_ROTATE_UR, 0'C
|
|
* 1: FB_ROTATE_CW, 90'C
|
|
* 2: FB_ROTATE_UD, 180'C
|
|
* 3: FB_ROTATE_CCW, 270'C
|
|
*/
|
|
static int jzfb_rotate_change( int angle )
|
|
{
|
|
struct fb_info *fb = &jzlcd_info->fb;
|
|
|
|
/* clear frame buffer */
|
|
memset((void*)lcd_frame_user_fb, 0x00, fb->fix.smem_len);
|
|
switch ( angle ) {
|
|
case FB_ROTATE_UR:
|
|
fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w;
|
|
fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h;
|
|
/* change lcd controller's data buffer to lcd_frame_user_fb*/
|
|
lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame_user_fb);
|
|
if ( rotate_angle != FB_ROTATE_UR )
|
|
kthread_stop(jzlcd_info->rotate_daemon_thread);
|
|
rotate_angle = angle;
|
|
break;
|
|
case FB_ROTATE_UD:
|
|
case FB_ROTATE_CW:
|
|
case FB_ROTATE_CCW:
|
|
if ( angle == FB_ROTATE_UD ) {
|
|
fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w;
|
|
fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h;
|
|
}
|
|
else { /* CW, CCW */
|
|
fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.h;
|
|
fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.w;
|
|
}
|
|
/* change lcd controller's data buffer to lcd_frame[0]*/
|
|
lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]);
|
|
if ( rotate_angle == FB_ROTATE_UR || \
|
|
jzlcd_info->rotate_daemon_thread == NULL)
|
|
jzlcd_info->rotate_daemon_thread = kthread_run( jzfb_rotate_daemon_thread, jzlcd_info, "%s", "jzlcd-rotate-daemon"); /* start rotate daemon */
|
|
rotate_angle = angle;
|
|
break;
|
|
default:
|
|
printk("Invalid angle(%d)\n", (unsigned int)angle);
|
|
}
|
|
fb->fix.line_length = fb->var.xres * CONFIG_JZLCD_FRAMEBUFFER_BPP/8;
|
|
dma_cache_wback_inv((unsigned int)(lcd_frame_desc0), sizeof(struct lcd_desc));
|
|
return 0;
|
|
}
|
|
|
|
void jzfb_fb_rotate(struct fb_info *fbi, int angle)
|
|
{
|
|
jzfb_rotate_change( angle/90 );
|
|
}
|
|
#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */
|
|
|
|
static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
|
|
{
|
|
chan &= 0xffff;
|
|
chan >>= 16 - bf->length;
|
|
return chan << bf->offset;
|
|
}
|
|
|
|
static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
|
|
u_int transp, struct fb_info *info)
|
|
{
|
|
struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info;
|
|
unsigned short *ptr, ctmp;
|
|
|
|
// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp);
|
|
if (regno >= NR_PALETTE)
|
|
return 1;
|
|
|
|
cfb->palette[regno].red = red ;
|
|
cfb->palette[regno].green = green;
|
|
cfb->palette[regno].blue = blue;
|
|
if (cfb->fb.var.bits_per_pixel <= 16) {
|
|
red >>= 8;
|
|
green >>= 8;
|
|
blue >>= 8;
|
|
|
|
red &= 0xff;
|
|
green &= 0xff;
|
|
blue &= 0xff;
|
|
}
|
|
switch (cfb->fb.var.bits_per_pixel) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) {
|
|
ctmp = (77L * red + 150L * green + 29L * blue) >> 8;
|
|
ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) |
|
|
(ctmp >> 3);
|
|
} else {
|
|
/* RGB 565 */
|
|
if (((red >> 3) == 0) && ((red >> 2) != 0))
|
|
red = 1 << 3;
|
|
if (((blue >> 3) == 0) && ((blue >> 2) != 0))
|
|
blue = 1 << 3;
|
|
ctmp = ((red >> 3) << 11)
|
|
| ((green >> 2) << 5) | (blue >> 3);
|
|
}
|
|
|
|
ptr = (unsigned short *)lcd_palette;
|
|
ptr = (unsigned short *)(((u32)ptr)|0xa0000000);
|
|
ptr[regno] = ctmp;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
if (regno < 16)
|
|
((u32 *)cfb->fb.pseudo_palette)[regno] =
|
|
((red >> 3) << 10) |
|
|
((green >> 3) << 5) |
|
|
(blue >> 3);
|
|
break;
|
|
case 16:
|
|
if (regno < 16) {
|
|
((u32 *)cfb->fb.pseudo_palette)[regno] =
|
|
((red >> 3) << 11) |
|
|
((green >> 2) << 5) |
|
|
(blue >> 3);
|
|
}
|
|
break;
|
|
case 18:
|
|
case 24:
|
|
case 32:
|
|
if (regno < 16)
|
|
((u32 *)cfb->fb.pseudo_palette)[regno] =
|
|
(red << 16) |
|
|
(green << 8) |
|
|
(blue << 0);
|
|
|
|
/* if (regno < 16) {
|
|
unsigned val;
|
|
val = chan_to_field(red, &cfb->fb.var.red);
|
|
val |= chan_to_field(green, &cfb->fb.var.green);
|
|
val |= chan_to_field(blue, &cfb->fb.var.blue);
|
|
((u32 *)cfb->fb.pseudo_palette)[regno] = val;
|
|
}
|
|
*/
|
|
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int jzfb_ioctl (struct fb_info *fb, unsigned int cmd, unsigned long arg )
|
|
{
|
|
int ret = 0;
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
switch (cmd) {
|
|
case FBIOSETBACKLIGHT:
|
|
__lcd_set_backlight_level(arg); /* We support 8 levels here. */
|
|
break;
|
|
case FBIODISPON:
|
|
__lcd_display_on();
|
|
break;
|
|
case FBIODISPOFF:
|
|
__lcd_display_off();
|
|
break;
|
|
case FBIOPRINT_REGS:
|
|
print_regs();
|
|
break;
|
|
case FBIOGETBUFADDRS:
|
|
if ( copy_to_user(argp, &jz_lcd_buffer_addrs,
|
|
sizeof(struct jz_lcd_buffer_addrs_t)) )
|
|
return -EFAULT;
|
|
break;
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
case FBIOROTATE:
|
|
ret = jzfb_rotate_change(arg);
|
|
break;
|
|
#endif /* defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */
|
|
default:
|
|
printk("Warn: Command(%x) not support\n", cmd);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
return ret;
|
|
|
|
}
|
|
|
|
/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */
|
|
static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
|
{
|
|
struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info;
|
|
unsigned long start;
|
|
unsigned long off;
|
|
u32 len;
|
|
|
|
off = vma->vm_pgoff << PAGE_SHIFT;
|
|
//fb->fb_get_fix(&fix, PROC_CONSOLE(info), info);
|
|
|
|
/* frame buffer memory */
|
|
start = cfb->fb.fix.smem_start;
|
|
len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len);
|
|
start &= PAGE_MASK;
|
|
|
|
if ((vma->vm_end - vma->vm_start + off) > len)
|
|
return -EINVAL;
|
|
off += start;
|
|
|
|
vma->vm_pgoff = off >> PAGE_SHIFT;
|
|
vma->vm_flags |= VM_IO;
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */
|
|
|
|
#if 1
|
|
pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
|
|
pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */
|
|
// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */
|
|
#endif
|
|
|
|
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
|
|
vma->vm_end - vma->vm_start,
|
|
vma->vm_page_prot)) {
|
|
return -EAGAIN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* checks var and eventually tweaks it to something supported,
|
|
* DO NOT MODIFY PAR */
|
|
static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
|
{
|
|
print_dbg("jzfb_check_var");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* set the video mode according to info->var
|
|
*/
|
|
static int jzfb_set_par(struct fb_info *info)
|
|
{
|
|
print_dbg("jzfb_set_par");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* (Un)Blank the display.
|
|
* Fix me: should we use VESA value?
|
|
*/
|
|
static int jzfb_blank(int blank_mode, struct fb_info *info)
|
|
{
|
|
|
|
dprintk("fb_blank %d %p", blank_mode, info);
|
|
|
|
switch (blank_mode) {
|
|
|
|
case FB_BLANK_UNBLANK:
|
|
//case FB_BLANK_NORMAL:
|
|
/* Turn on panel */
|
|
__lcd_set_ena();
|
|
__lcd_display_on();
|
|
break;
|
|
|
|
case FB_BLANK_NORMAL:
|
|
case FB_BLANK_VSYNC_SUSPEND:
|
|
case FB_BLANK_HSYNC_SUSPEND:
|
|
case FB_BLANK_POWERDOWN:
|
|
#if 0
|
|
/* Turn off panel */
|
|
__lcd_set_dis();
|
|
__lcd_display_off();
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* pan display
|
|
*/
|
|
static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
|
|
{
|
|
struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info;
|
|
int dy;
|
|
|
|
if (!var || !cfb) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (var->xoffset - cfb->fb.var.xoffset) {
|
|
/* No support for X panning for now! */
|
|
return -EINVAL;
|
|
}
|
|
|
|
dy = var->yoffset - cfb->fb.var.yoffset;
|
|
print_dbg("var.yoffset: %d", dy);
|
|
if (dy) {
|
|
|
|
print_dbg("Panning screen of %d lines", dy);
|
|
|
|
lcd_frame_desc0->databuf += (cfb->fb.fix.line_length * dy);
|
|
/* TODO: Wait for current frame to finished */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */
|
|
static struct fb_ops jzfb_ops = {
|
|
.owner = THIS_MODULE,
|
|
.fb_setcolreg = jzfb_setcolreg,
|
|
.fb_check_var = jzfb_check_var,
|
|
.fb_set_par = jzfb_set_par,
|
|
.fb_blank = jzfb_blank,
|
|
.fb_pan_display = jzfb_pan_display,
|
|
.fb_fillrect = cfb_fillrect,
|
|
.fb_copyarea = cfb_copyarea,
|
|
.fb_imageblit = cfb_imageblit,
|
|
.fb_mmap = jzfb_mmap,
|
|
.fb_ioctl = jzfb_ioctl,
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
.fb_rotate = jzfb_fb_rotate,
|
|
#endif
|
|
};
|
|
|
|
static int jzfb_set_var(struct fb_var_screeninfo *var, int con,
|
|
struct fb_info *info)
|
|
{
|
|
struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info;
|
|
int chgvar = 0;
|
|
|
|
var->height = jzfb.h ;
|
|
var->width = jzfb.w ;
|
|
var->bits_per_pixel = jzfb.bpp;
|
|
|
|
var->vmode = FB_VMODE_NONINTERLACED;
|
|
var->activate = cfb->fb.var.activate;
|
|
var->xres = var->width;
|
|
var->yres = var->height;
|
|
var->xres_virtual = var->width;
|
|
var->yres_virtual = var->height;
|
|
var->xoffset = 0;
|
|
var->yoffset = 0;
|
|
var->pixclock = 0;
|
|
var->left_margin = 0;
|
|
var->right_margin = 0;
|
|
var->upper_margin = 0;
|
|
var->lower_margin = 0;
|
|
var->hsync_len = 0;
|
|
var->vsync_len = 0;
|
|
var->sync = 0;
|
|
var->activate &= ~FB_ACTIVATE_TEST;
|
|
|
|
/*
|
|
* CONUPDATE and SMOOTH_XPAN are equal. However,
|
|
* SMOOTH_XPAN is only used internally by fbcon.
|
|
*/
|
|
if (var->vmode & FB_VMODE_CONUPDATE) {
|
|
var->vmode |= FB_VMODE_YWRAP;
|
|
var->xoffset = cfb->fb.var.xoffset;
|
|
var->yoffset = cfb->fb.var.yoffset;
|
|
}
|
|
|
|
if (var->activate & FB_ACTIVATE_TEST)
|
|
return 0;
|
|
|
|
if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
|
|
return -EINVAL;
|
|
|
|
if (cfb->fb.var.xres != var->xres)
|
|
chgvar = 1;
|
|
if (cfb->fb.var.yres != var->yres)
|
|
chgvar = 1;
|
|
if (cfb->fb.var.xres_virtual != var->xres_virtual)
|
|
chgvar = 1;
|
|
if (cfb->fb.var.yres_virtual != var->yres_virtual)
|
|
chgvar = 1;
|
|
if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
|
|
chgvar = 1;
|
|
|
|
var->red.msb_right = 0;
|
|
var->green.msb_right = 0;
|
|
var->blue.msb_right = 0;
|
|
|
|
switch(var->bits_per_pixel){
|
|
case 1: /* Mono */
|
|
cfb->fb.fix.visual = FB_VISUAL_MONO01;
|
|
cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8;
|
|
break;
|
|
case 2: /* Mono */
|
|
var->red.offset = 0;
|
|
var->red.length = 2;
|
|
var->green.offset = 0;
|
|
var->green.length = 2;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 2;
|
|
|
|
cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
|
|
cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8;
|
|
break;
|
|
case 4: /* PSEUDOCOLOUR*/
|
|
var->red.offset = 0;
|
|
var->red.length = 4;
|
|
var->green.offset = 0;
|
|
var->green.length = 4;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 4;
|
|
|
|
cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
|
|
cfb->fb.fix.line_length = var->xres / 2;
|
|
break;
|
|
case 8: /* PSEUDOCOLOUR, 256 */
|
|
var->red.offset = 0;
|
|
var->red.length = 8;
|
|
var->green.offset = 0;
|
|
var->green.length = 8;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 8;
|
|
|
|
cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
|
|
cfb->fb.fix.line_length = var->xres ;
|
|
break;
|
|
case 15: /* DIRECTCOLOUR, 32k */
|
|
var->bits_per_pixel = 15;
|
|
var->red.offset = 10;
|
|
var->red.length = 5;
|
|
var->green.offset = 5;
|
|
var->green.length = 5;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 5;
|
|
|
|
cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR;
|
|
cfb->fb.fix.line_length = var->xres_virtual * 2;
|
|
break;
|
|
case 16: /* DIRECTCOLOUR, 64k */
|
|
var->bits_per_pixel = 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;
|
|
|
|
cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
|
|
cfb->fb.fix.line_length = var->xres_virtual * 2;
|
|
break;
|
|
case 18:
|
|
case 24:
|
|
case 32:
|
|
/* DIRECTCOLOUR, 16M */
|
|
var->bits_per_pixel = 32;
|
|
|
|
var->red.offset = 16;
|
|
var->red.length = 8;
|
|
var->green.offset = 8;
|
|
var->green.length = 8;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 8;
|
|
var->transp.offset = 24;
|
|
var->transp.length = 8;
|
|
|
|
cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
|
|
cfb->fb.fix.line_length = var->xres_virtual * 4;
|
|
break;
|
|
|
|
default: /* in theory this should never happen */
|
|
printk(KERN_WARNING "%s: don't support for %dbpp\n",
|
|
cfb->fb.fix.id, var->bits_per_pixel);
|
|
break;
|
|
}
|
|
|
|
cfb->fb.var = *var;
|
|
cfb->fb.var.activate &= ~FB_ACTIVATE_ALL;
|
|
|
|
/*
|
|
* Update the old var. The fbcon drivers still use this.
|
|
* Once they are using cfb->fb.var, this can be dropped.
|
|
* --rmk
|
|
*/
|
|
//display->var = cfb->fb.var;
|
|
/*
|
|
* If we are setting all the virtual consoles, also set the
|
|
* defaults used to create new consoles.
|
|
*/
|
|
fb_set_cmap(&cfb->fb.cmap, &cfb->fb);
|
|
dprintk("jzfb_set_var: after fb_set_cmap...\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct lcd_cfb_info * jzfb_alloc_fb_info(void)
|
|
{
|
|
struct lcd_cfb_info *cfb;
|
|
|
|
cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL);
|
|
|
|
if (!cfb)
|
|
return NULL;
|
|
|
|
jzlcd_info = cfb;
|
|
|
|
memset(cfb, 0, sizeof(struct lcd_cfb_info) );
|
|
|
|
cfb->currcon = -1;
|
|
|
|
|
|
strcpy(cfb->fb.fix.id, "jz-lcd");
|
|
cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
|
|
cfb->fb.fix.type_aux = 0;
|
|
cfb->fb.fix.xpanstep = 1;
|
|
cfb->fb.fix.ypanstep = 1;
|
|
cfb->fb.fix.ywrapstep = 0;
|
|
cfb->fb.fix.accel = FB_ACCEL_NONE;
|
|
|
|
cfb->fb.var.nonstd = 0;
|
|
cfb->fb.var.activate = FB_ACTIVATE_NOW;
|
|
cfb->fb.var.height = -1;
|
|
cfb->fb.var.width = -1;
|
|
cfb->fb.var.accel_flags = FB_ACCELF_TEXT;
|
|
|
|
cfb->fb.fbops = &jzfb_ops;
|
|
cfb->fb.flags = FBINFO_FLAG_DEFAULT;
|
|
|
|
cfb->fb.pseudo_palette = (void *)(cfb + 1);
|
|
|
|
switch (jzfb.bpp) {
|
|
case 1:
|
|
fb_alloc_cmap(&cfb->fb.cmap, 4, 0);
|
|
break;
|
|
case 2:
|
|
fb_alloc_cmap(&cfb->fb.cmap, 8, 0);
|
|
break;
|
|
case 4:
|
|
fb_alloc_cmap(&cfb->fb.cmap, 32, 0);
|
|
break;
|
|
case 8:
|
|
|
|
default:
|
|
fb_alloc_cmap(&cfb->fb.cmap, 256, 0);
|
|
break;
|
|
}
|
|
dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len);
|
|
|
|
return cfb;
|
|
}
|
|
|
|
/*
|
|
* Map screen memory
|
|
*/
|
|
static int jzfb_map_smem(struct lcd_cfb_info *cfb)
|
|
{
|
|
struct page * map = NULL;
|
|
unsigned char *tmp;
|
|
unsigned int page_shift, needroom, t;
|
|
#if defined(CONFIG_SOC_JZ4740)
|
|
if (jzfb.bpp == 18 || jzfb.bpp == 24)
|
|
t = 32;
|
|
else
|
|
t = jzfb.bpp;
|
|
#else
|
|
if (jzfb.bpp == 15)
|
|
t = 16;
|
|
else
|
|
t = jzfb.bpp;
|
|
#endif
|
|
|
|
needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h;
|
|
for (page_shift = 0; page_shift < 12; page_shift++)
|
|
if ((PAGE_SIZE << page_shift) >= needroom)
|
|
break;
|
|
|
|
/* lcd_palette room total 4KB:
|
|
* 0 -- 512: lcd palette
|
|
* 1024 -- [1024+16*3]: lcd descripters
|
|
* [1024+16*3] -- 4096: reserved
|
|
*/
|
|
lcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0);
|
|
if ((!lcd_palette))
|
|
return -ENOMEM;
|
|
|
|
memset((void *)lcd_palette, 0, PAGE_SIZE);
|
|
map = virt_to_page(lcd_palette);
|
|
set_bit(PG_reserved, &map->flags);
|
|
lcd_desc_base = (struct lcd_desc *)(lcd_palette + 1024);
|
|
|
|
jz_lcd_buffer_addrs.fb_num = CONFIG_JZLCD_FRAMEBUFFER_MAX;
|
|
printk("jzlcd use %d framebuffer:\n", CONFIG_JZLCD_FRAMEBUFFER_MAX);
|
|
/* alloc frame buffer space */
|
|
for ( t = 0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) {
|
|
lcd_frame[t] = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift);
|
|
if ((!lcd_frame[t])) {
|
|
printk("no mem for fb[%d]\n", t);
|
|
return -ENOMEM;
|
|
}
|
|
// memset((void *)lcd_frame[t], 0, PAGE_SIZE << page_shift);
|
|
for (tmp=(unsigned char *)lcd_frame[t];
|
|
tmp < lcd_frame[t] + (PAGE_SIZE << page_shift);
|
|
tmp += PAGE_SIZE) {
|
|
map = virt_to_page(tmp);
|
|
set_bit(PG_reserved, &map->flags);
|
|
}
|
|
jz_lcd_buffer_addrs.fb_phys_addr[t] = virt_to_phys((void *)lcd_frame[t]);
|
|
printk("jzlcd fb[%d] phys addr =0x%08x\n",
|
|
t, jz_lcd_buffer_addrs.fb_phys_addr[t]);
|
|
}
|
|
#if !defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame[0]);
|
|
cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift);
|
|
cfb->fb.screen_base =
|
|
(unsigned char *)(((unsigned int)lcd_frame[0] & 0x1fffffff) | 0xa0000000);
|
|
#else /* Framebuffer rotate */
|
|
lcd_frame_user_fb = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift);
|
|
if ((!lcd_frame_user_fb)) {
|
|
printk("no mem for fb[%d]\n", t);
|
|
return -ENOMEM;
|
|
}
|
|
memset((void *)lcd_frame_user_fb, 0, PAGE_SIZE << page_shift);
|
|
for (tmp=(unsigned char *)lcd_frame_user_fb;
|
|
tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift);
|
|
tmp += PAGE_SIZE) {
|
|
map = virt_to_page(tmp);
|
|
set_bit(PG_reserved, &map->flags);
|
|
}
|
|
|
|
printk("Rotate userfb phys addr =0x%08x\n",
|
|
(unsigned int)virt_to_phys((void *)lcd_frame_user_fb));
|
|
cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame_user_fb);
|
|
cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift);
|
|
cfb->fb.screen_base = (unsigned char *)(((unsigned int)lcd_frame_user_fb & 0x1fffffff) | 0xa0000000);
|
|
|
|
#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */
|
|
if (!cfb->fb.screen_base) {
|
|
printk("%s: unable to map screen memory\n", cfb->fb.fix.id);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void jzfb_free_fb_info(struct lcd_cfb_info *cfb)
|
|
{
|
|
if (cfb) {
|
|
fb_alloc_cmap(&cfb->fb.cmap, 0, 0);
|
|
kfree(cfb);
|
|
}
|
|
}
|
|
|
|
static void jzfb_unmap_smem(struct lcd_cfb_info *cfb)
|
|
{
|
|
struct page * map = NULL;
|
|
unsigned char *tmp;
|
|
unsigned int page_shift, needroom, t;
|
|
#if defined(CONFIG_SOC_JZ4740)
|
|
if (jzfb.bpp == 18 || jzfb.bpp == 24)
|
|
t = 32;
|
|
else
|
|
t = jzfb.bpp;
|
|
#else
|
|
if (jzfb.bpp == 15)
|
|
t = 16;
|
|
else
|
|
t = jzfb.bpp;
|
|
#endif
|
|
needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h;
|
|
for (page_shift = 0; page_shift < 12; page_shift++)
|
|
if ((PAGE_SIZE << page_shift) >= needroom)
|
|
break;
|
|
|
|
if (cfb && cfb->fb.screen_base) {
|
|
iounmap(cfb->fb.screen_base);
|
|
cfb->fb.screen_base = NULL;
|
|
release_mem_region(cfb->fb.fix.smem_start,
|
|
cfb->fb.fix.smem_len);
|
|
}
|
|
|
|
if (lcd_palette) {
|
|
map = virt_to_page(lcd_palette);
|
|
clear_bit(PG_reserved, &map->flags);
|
|
free_pages((int)lcd_palette, 0);
|
|
}
|
|
|
|
for ( t=0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) {
|
|
if (lcd_frame[t]) {
|
|
for (tmp=(unsigned char *)lcd_frame[t];
|
|
tmp < lcd_frame[t] + (PAGE_SIZE << page_shift);
|
|
tmp += PAGE_SIZE) {
|
|
map = virt_to_page(tmp);
|
|
clear_bit(PG_reserved, &map->flags);
|
|
}
|
|
free_pages((int)lcd_frame[t], page_shift);
|
|
}
|
|
}
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
if (lcd_frame_user_fb) {
|
|
for (tmp=(unsigned char *)lcd_frame_user_fb;
|
|
tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift);
|
|
tmp += PAGE_SIZE) {
|
|
map = virt_to_page(tmp);
|
|
clear_bit(PG_reserved, &map->flags);
|
|
}
|
|
free_pages((int)lcd_frame_user_fb, page_shift);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
static void lcd_descriptor_init(void)
|
|
{
|
|
int i;
|
|
unsigned int pal_size;
|
|
unsigned int frm_size, ln_size;
|
|
unsigned char dual_panel = 0;
|
|
|
|
i = jzfb.bpp;
|
|
#if defined(CONFIG_SOC_JZ4740)
|
|
if (i == 18 || i == 24)
|
|
i = 32;
|
|
#else
|
|
if (i == 15)
|
|
i = 16;
|
|
#endif
|
|
frm_size = (jzfb.w*jzfb.h*i)>>3;
|
|
ln_size = (jzfb.w*i)>>3;
|
|
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) {
|
|
dual_panel = 1;
|
|
frm_size >>= 1;
|
|
}
|
|
|
|
frm_size = frm_size / 4;
|
|
ln_size = ln_size / 4;
|
|
|
|
switch (jzfb.bpp) {
|
|
case 1:
|
|
pal_size = 4;
|
|
break;
|
|
case 2:
|
|
pal_size = 8;
|
|
break;
|
|
case 4:
|
|
pal_size = 32;
|
|
break;
|
|
case 8:
|
|
default:
|
|
pal_size = 512;
|
|
}
|
|
|
|
pal_size /= 4;
|
|
|
|
lcd_frame_desc0 = lcd_desc_base + 0;
|
|
lcd_frame_desc1 = lcd_desc_base + 1;
|
|
lcd_palette_desc = lcd_desc_base + 2;
|
|
|
|
jz_lcd_buffer_addrs.lcd_desc_phys_addr = (unsigned int)virt_to_phys(lcd_frame_desc0);
|
|
|
|
/* Palette Descriptor */
|
|
lcd_palette_desc->next_desc = (int)virt_to_phys(lcd_frame_desc0);
|
|
lcd_palette_desc->databuf = (int)virt_to_phys((void *)lcd_palette);
|
|
lcd_palette_desc->frame_id = (unsigned int)0xdeadbeaf;
|
|
lcd_palette_desc->cmd = pal_size|LCD_CMD_PAL; /* Palette Descriptor */
|
|
|
|
/* Frame Descriptor 0 */
|
|
if (jzfb.bpp <= 8)
|
|
lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_palette_desc);
|
|
else
|
|
lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_frame_desc0);
|
|
lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]);
|
|
lcd_frame_desc0->frame_id = (unsigned int)0xbeafbeaf;
|
|
lcd_frame_desc0->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size;
|
|
dma_cache_wback_inv((unsigned int)(lcd_palette_desc),0x10);
|
|
dma_cache_wback_inv((unsigned int)(lcd_frame_desc0),0x10);
|
|
|
|
if (!(dual_panel))
|
|
return;
|
|
|
|
/* Frame Descriptor 1 */
|
|
lcd_frame_desc1->next_desc = (int)virt_to_phys(lcd_frame_desc1);
|
|
lcd_frame_desc1->databuf = virt_to_phys((void *)(lcd_frame[0] + frm_size * 4));
|
|
lcd_frame_desc1->frame_id = (unsigned int)0xdeaddead;
|
|
lcd_frame_desc1->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size;
|
|
dma_cache_wback_inv((unsigned int)(lcd_frame_desc1),0x10);
|
|
}
|
|
|
|
static int lcd_hw_init(void)
|
|
{
|
|
unsigned int val = 0;
|
|
unsigned int pclk;
|
|
unsigned int stnH;
|
|
int ret = 0;
|
|
|
|
/* Setting Control register */
|
|
switch (jzfb.bpp) {
|
|
case 1:
|
|
val |= LCD_CTRL_BPP_1;
|
|
break;
|
|
case 2:
|
|
val |= LCD_CTRL_BPP_2;
|
|
break;
|
|
case 4:
|
|
val |= LCD_CTRL_BPP_4;
|
|
break;
|
|
case 8:
|
|
val |= LCD_CTRL_BPP_8;
|
|
break;
|
|
case 15:
|
|
val |= LCD_CTRL_RGB555;
|
|
case 16:
|
|
val |= LCD_CTRL_BPP_16;
|
|
break;
|
|
#if defined(CONFIG_SOC_JZ4740)
|
|
case 17 ... 32:
|
|
val |= LCD_CTRL_BPP_18_24; /* target is 4bytes/pixel */
|
|
break;
|
|
#endif
|
|
default:
|
|
printk("The BPP %d is not supported\n", jzfb.bpp);
|
|
val |= LCD_CTRL_BPP_16;
|
|
break;
|
|
}
|
|
|
|
switch (jzfb.cfg & MODE_MASK) {
|
|
case MODE_STN_MONO_DUAL:
|
|
case MODE_STN_COLOR_DUAL:
|
|
case MODE_STN_MONO_SINGLE:
|
|
case MODE_STN_COLOR_SINGLE:
|
|
switch (jzfb.bpp) {
|
|
case 1:
|
|
case 2:
|
|
val |= LCD_CTRL_FRC_2;
|
|
break;
|
|
case 4:
|
|
val |= LCD_CTRL_FRC_4;
|
|
break;
|
|
case 8:
|
|
default:
|
|
val |= LCD_CTRL_FRC_16;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
val |= LCD_CTRL_BST_16; /* Burst Length is 16WORD=64Byte */
|
|
|
|
switch (jzfb.cfg & MODE_MASK) {
|
|
case MODE_STN_MONO_DUAL:
|
|
case MODE_STN_COLOR_DUAL:
|
|
case MODE_STN_MONO_SINGLE:
|
|
case MODE_STN_COLOR_SINGLE:
|
|
switch (jzfb.cfg & STN_DAT_PINMASK) {
|
|
#define align2(n) (n)=((((n)+1)>>1)<<1)
|
|
#define align4(n) (n)=((((n)+3)>>2)<<2)
|
|
#define align8(n) (n)=((((n)+7)>>3)<<3)
|
|
case STN_DAT_PIN1:
|
|
/* Do not adjust the hori-param value. */
|
|
break;
|
|
case STN_DAT_PIN2:
|
|
align2(jzfb.hsw);
|
|
align2(jzfb.elw);
|
|
align2(jzfb.blw);
|
|
break;
|
|
case STN_DAT_PIN4:
|
|
align4(jzfb.hsw);
|
|
align4(jzfb.elw);
|
|
align4(jzfb.blw);
|
|
break;
|
|
case STN_DAT_PIN8:
|
|
align8(jzfb.hsw);
|
|
align8(jzfb.elw);
|
|
align8(jzfb.blw);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
val |= 1 << 26; /* Output FIFO underrun protection */
|
|
REG_LCD_CTRL = val;
|
|
|
|
switch (jzfb.cfg & MODE_MASK) {
|
|
case MODE_STN_MONO_DUAL:
|
|
case MODE_STN_COLOR_DUAL:
|
|
case MODE_STN_MONO_SINGLE:
|
|
case MODE_STN_COLOR_SINGLE:
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL))
|
|
stnH = jzfb.h >> 1;
|
|
else
|
|
stnH = jzfb.h;
|
|
|
|
REG_LCD_VSYNC = (0 << 16) | jzfb.vsw;
|
|
REG_LCD_HSYNC = ((jzfb.blw+jzfb.w) << 16) | (jzfb.blw+jzfb.w+jzfb.hsw);
|
|
|
|
/* Screen setting */
|
|
REG_LCD_VAT = ((jzfb.blw + jzfb.w + jzfb.hsw + jzfb.elw) << 16) | (stnH + jzfb.vsw + jzfb.bfw + jzfb.efw);
|
|
REG_LCD_DAH = (jzfb.blw << 16) | (jzfb.blw + jzfb.w);
|
|
REG_LCD_DAV = (0 << 16) | (stnH);
|
|
|
|
/* AC BIAs signal */
|
|
REG_LCD_PS = (0 << 16) | (stnH+jzfb.vsw+jzfb.efw+jzfb.bfw);
|
|
|
|
break;
|
|
|
|
case MODE_TFT_GEN:
|
|
case MODE_TFT_SHARP:
|
|
case MODE_TFT_CASIO:
|
|
case MODE_TFT_SAMSUNG:
|
|
case MODE_8BIT_SERIAL_TFT:
|
|
case MODE_TFT_18BIT:
|
|
REG_LCD_VSYNC = (0 << 16) | jzfb.vsw;
|
|
#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)
|
|
REG_LCD_DAV = (0 << 16) | ( jzfb.h );
|
|
#else
|
|
REG_LCD_DAV = ((jzfb.vsw + jzfb.bfw) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h);
|
|
#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/
|
|
REG_LCD_VAT = (((jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw)) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h + jzfb.efw);
|
|
REG_LCD_HSYNC = (0 << 16) | jzfb.hsw;
|
|
REG_LCD_DAH = ((jzfb.hsw + jzfb.blw) << 16) | (jzfb.hsw + jzfb.blw + jzfb.w);
|
|
break;
|
|
}
|
|
|
|
switch (jzfb.cfg & MODE_MASK) {
|
|
case MODE_TFT_SAMSUNG:
|
|
{
|
|
unsigned int total, tp_s, tp_e, ckv_s, ckv_e;
|
|
unsigned int rev_s, rev_e, inv_s, inv_e;
|
|
total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw;
|
|
tp_s = jzfb.blw + jzfb.w + 1;
|
|
tp_e = tp_s + 1;
|
|
ckv_s = tp_s - jz_clocks.pixclk/(1000000000/4100);
|
|
ckv_e = tp_s + total;
|
|
rev_s = tp_s - 11; /* -11.5 clk */
|
|
rev_e = rev_s + total;
|
|
inv_s = tp_s;
|
|
inv_e = inv_s + total;
|
|
REG_LCD_CLS = (tp_s << 16) | tp_e;
|
|
REG_LCD_PS = (ckv_s << 16) | ckv_e;
|
|
REG_LCD_SPL = (rev_s << 16) | rev_e;
|
|
REG_LCD_REV = (inv_s << 16) | inv_e;
|
|
jzfb.cfg |= STFT_REVHI | STFT_SPLHI;
|
|
break;
|
|
}
|
|
case MODE_TFT_SHARP:
|
|
{
|
|
unsigned int total, cls_s, cls_e, ps_s, ps_e;
|
|
unsigned int spl_s, spl_e, rev_s, rev_e;
|
|
total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw;
|
|
#if !defined(CONFIG_JZLCD_INNOLUX_AT080TN42)
|
|
spl_s = 1;
|
|
spl_e = spl_s + 1;
|
|
cls_s = 0;
|
|
cls_e = total - 60; /* > 4us (pclk = 80ns) */
|
|
ps_s = cls_s;
|
|
ps_e = cls_e;
|
|
rev_s = total - 40; /* > 3us (pclk = 80ns) */
|
|
rev_e = rev_s + total;
|
|
jzfb.cfg |= STFT_PSHI;
|
|
#else /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/
|
|
spl_s = total - 5; /* LD */
|
|
spl_e = total - 3;
|
|
cls_s = 32; /* CKV */
|
|
cls_e = 145;
|
|
ps_s = 0; /* OEV */
|
|
ps_e = 45;
|
|
rev_s = 0; /* POL */
|
|
rev_e = 0;
|
|
#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/
|
|
REG_LCD_SPL = (spl_s << 16) | spl_e;
|
|
REG_LCD_CLS = (cls_s << 16) | cls_e;
|
|
REG_LCD_PS = (ps_s << 16) | ps_e;
|
|
REG_LCD_REV = (rev_s << 16) | rev_e;
|
|
break;
|
|
}
|
|
case MODE_TFT_CASIO:
|
|
break;
|
|
}
|
|
|
|
/* Configure the LCD panel */
|
|
REG_LCD_CFG = jzfb.cfg;
|
|
|
|
/* Timing setting */
|
|
__cpm_stop_lcd();
|
|
|
|
val = jzfb.fclk; /* frame clk */
|
|
|
|
if ( (jzfb.cfg & MODE_MASK) != MODE_8BIT_SERIAL_TFT) {
|
|
pclk = val * (jzfb.w + jzfb.hsw + jzfb.elw + jzfb.blw) *
|
|
(jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */
|
|
}
|
|
else {
|
|
/* serial mode: Hsync period = 3*Width_Pixel */
|
|
pclk = val * (jzfb.w*3 + jzfb.hsw + jzfb.elw + jzfb.blw) *
|
|
(jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */
|
|
}
|
|
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL))
|
|
pclk = (pclk * 3);
|
|
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL))
|
|
pclk = pclk >> ((jzfb.cfg & STN_DAT_PINMASK) >> 4);
|
|
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL))
|
|
pclk >>= 1;
|
|
#if defined(CONFIG_SOC_JZ4730)
|
|
val = __cpm_get_pllout() / pclk;
|
|
REG_CPM_CFCR2 = val - 1;
|
|
val = __cpm_get_pllout() / (pclk * 4);
|
|
val = __cpm_divisor_encode(val);
|
|
__cpm_set_lcdclk_div(val);
|
|
REG_CPM_CFCR |= CPM_CFCR_UPE;
|
|
#elif defined(CONFIG_SOC_JZ4740)
|
|
val = ( __cpm_get_pllout2()) / pclk;
|
|
val--;
|
|
if ( val > 0x3ff ) {
|
|
printk("pixel clock divid is too large, set it to 0x3ff\n");
|
|
val = 0x3ff;
|
|
}
|
|
__cpm_set_pixdiv(val);
|
|
|
|
val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */
|
|
val =__cpm_get_pllout() / val;
|
|
if ( val > 0x1f ) {
|
|
printk("lcd clock divide is too large, set it to 0x1f\n");
|
|
val = 0x1f;
|
|
}
|
|
__cpm_set_ldiv( val );
|
|
REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */
|
|
|
|
#else
|
|
printk("drivers/video/Jzlcd.c, CONFIG_MIPS, please set chip type.\n");
|
|
#endif /*#ifdef CONFIG_MIPS_JZ4730 */
|
|
|
|
jz_clocks.pixclk = __cpm_get_pixclk();
|
|
jz_clocks.lcdclk = __cpm_get_lcdclk();
|
|
printk("LCDC: PixClock:%d LcdClock:%d\n",
|
|
jz_clocks.pixclk, jz_clocks.lcdclk);
|
|
|
|
__cpm_start_lcd();
|
|
udelay(1000);
|
|
return ret;
|
|
}
|
|
|
|
static irqreturn_t lcd_interrupt_handler(int irq, void *dev_id)
|
|
{
|
|
unsigned int state;
|
|
|
|
state = REG_LCD_STATE;
|
|
|
|
if (state & LCD_STATE_EOF) /* End of frame */
|
|
REG_LCD_STATE = state & ~LCD_STATE_EOF;
|
|
|
|
if (state & LCD_STATE_IFU0) {
|
|
dprintk("InFiFo0 underrun\n");
|
|
REG_LCD_STATE = state & ~LCD_STATE_IFU0;
|
|
}
|
|
|
|
if (state & LCD_STATE_OFU) { /* Out fifo underrun */
|
|
REG_LCD_STATE = state & ~LCD_STATE_OFU;
|
|
dprintk("Out FiFo underrun.\n");
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
/*
|
|
* Suspend the LCDC.
|
|
*/
|
|
static int jzfb_suspend(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
__lcd_clr_ena(); /* Quick Disable */
|
|
__lcd_display_off();
|
|
__cpm_stop_lcd();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Resume the LCDC.
|
|
*/
|
|
#ifdef CONFIG_SOC_JZ4730
|
|
static int jzfb_resume(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
__cpm_start_lcd();
|
|
|
|
__lcd_display_pin_init();
|
|
|
|
__lcd_display_on();
|
|
|
|
lcd_hw_init();
|
|
|
|
if (jzfb.bpp <= 8)
|
|
REG_LCD_DA0 = virt_to_phys(lcd_palette_desc);
|
|
else
|
|
REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0);
|
|
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL))
|
|
REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1);
|
|
|
|
__lcd_set_ena();
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
/*
|
|
* Resume the LCDC.
|
|
*/
|
|
static int jzfb_resume(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
__cpm_start_lcd();
|
|
__gpio_set_pin(GPIO_DISP_OFF_N);
|
|
__lcd_special_on();
|
|
__lcd_set_ena();
|
|
mdelay(200);
|
|
__lcd_set_backlight_level(80);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_MIPS_JZ4730 */
|
|
#else /* CONFIG_PM */
|
|
#define jzfb_suspend NULL
|
|
#define jzfb_resume NULL
|
|
#endif /* CONFIG_PM */
|
|
|
|
static int __init jzfb_probe(struct platform_device *pdev)
|
|
{
|
|
struct lcd_cfb_info *cfb;
|
|
int err = 0;
|
|
|
|
/* In special mode, we only need init special pin,
|
|
* as general lcd pin has init in uboot */
|
|
#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750)
|
|
switch (jzfb.cfg & MODE_MASK) {
|
|
case LCD_CFG_MODE_SPECIAL_TFT_1:
|
|
case LCD_CFG_MODE_SPECIAL_TFT_2:
|
|
case LCD_CFG_MODE_SPECIAL_TFT_3:
|
|
__gpio_as_lcd_special();
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
#endif
|
|
__lcd_display_pin_init();
|
|
|
|
cfb = jzfb_alloc_fb_info();
|
|
if (!cfb)
|
|
goto failed;
|
|
|
|
err = jzfb_map_smem(cfb);
|
|
if (err)
|
|
goto failed;
|
|
|
|
jzfb_set_var(&cfb->fb.var, -1, &cfb->fb);
|
|
|
|
lcd_descriptor_init();
|
|
|
|
err = lcd_hw_init();
|
|
if (err)
|
|
goto failed;
|
|
|
|
if (jzfb.bpp <= 8)
|
|
REG_LCD_DA0 = virt_to_phys(lcd_palette_desc);
|
|
else
|
|
REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0);
|
|
|
|
if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
|
|
((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL))
|
|
REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1);
|
|
|
|
__lcd_set_ena();
|
|
|
|
if (request_irq(IRQ_LCD, lcd_interrupt_handler, IRQF_DISABLED,
|
|
"lcd", 0)) {
|
|
err = -EBUSY;
|
|
goto failed;
|
|
}
|
|
|
|
__lcd_enable_ofu_intr(); /* enable OutFifo underrun */
|
|
// __lcd_enable_ifu0_intr(); /* needn't enable InFifo underrun */
|
|
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
jzfb_rotate_change(rotate_angle);
|
|
/* sleep n??? */
|
|
#endif
|
|
err = register_framebuffer(&cfb->fb);
|
|
if (err < 0) {
|
|
dprintk("jzfb_init(): register framebuffer err.\n");
|
|
goto failed;
|
|
}
|
|
|
|
printk("fb%d: %s frame buffer device, using %dK of video memory\n",
|
|
cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10);
|
|
|
|
__lcd_display_on();
|
|
|
|
return 0;
|
|
|
|
failed:
|
|
jzfb_unmap_smem(cfb);
|
|
jzfb_free_fb_info(cfb);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int jzfb_remove(struct device *dev)
|
|
{
|
|
struct lcd_cfb_info *cfb = dev_get_drvdata(dev);
|
|
jzfb_unmap_smem(cfb);
|
|
jzfb_free_fb_info(cfb);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver jz_lcd_driver = {
|
|
.probe = jzfb_probe,
|
|
.remove = jzfb_remove,
|
|
#ifdef CONFIG_PM
|
|
.suspend = jzfb_suspend,
|
|
.resume = jzfb_resume,
|
|
#endif
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
},
|
|
};
|
|
|
|
static int __init jzfb_init(void)
|
|
{
|
|
return platform_driver_register(&jz_lcd_driver);
|
|
}
|
|
|
|
static void __exit jzfb_cleanup(void)
|
|
{
|
|
#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)
|
|
kthread_stop(jzlcd_info->rotate_daemon_thread);
|
|
#endif
|
|
platform_driver_unregister(&jz_lcd_driver);
|
|
}
|
|
|
|
module_init(jzfb_init);
|
|
module_exit(jzfb_cleanup);
|
|
|
|
MODULE_DESCRIPTION("JzSOC LCD Controller driver");
|
|
MODULE_LICENSE("GPL");
|