mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-11-25 14:18:05 +02:00
dc3d3f1c49
it's basically also provided by ingenic and nativly based on 2.6.27, adjusted to fit into the OpenWrt-environment
1335 lines
32 KiB
C
Executable File
1335 lines
32 KiB
C
Executable File
/*
|
|
* linux/drivers/video/jzslcd.c -- Ingenic On-Chip Smart 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/pm_legacy.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 "jz4740_slcd.h"
|
|
|
|
#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
|
|
|
|
static jz_dma_desc slcd_palette_desc __attribute__ ((aligned (16)));
|
|
static jz_dma_desc slcd_frame_desc __attribute__ ((aligned (16)));
|
|
|
|
static int dma_chan;
|
|
static dma_addr_t slcd_frame_desc_phys_addr, slcd_palette_desc_phys_addr;
|
|
|
|
static unsigned char non_link_desp = 0;
|
|
static unsigned char is_set_reg = 0;
|
|
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
|
|
};
|
|
|
|
struct slcd_reg_info {
|
|
unsigned int cmd;
|
|
unsigned int data;
|
|
};
|
|
static struct slcd_reg_info reg_buf;
|
|
static struct lcd_cfb_info *jzslcd_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 bus;
|
|
unsigned int pclk; /* pixel clk */
|
|
|
|
};
|
|
|
|
static struct jzfb_info jzfb = {
|
|
#ifdef CONFIG_JZ_SLCD_LGDP4551
|
|
SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL,
|
|
400, 240, 16, 8, 16000000 /*16 bpp, 8 bus*/
|
|
// 240, 400, 18, 8, 16000000 /*18 bpp, 8 bus*/
|
|
// 400, 240, 18, 8, 16000000 /*18 bpp, 8 bus*/
|
|
#endif
|
|
|
|
#ifdef CONFIG_JZ_SLCD_SPFD5420A
|
|
SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL,
|
|
400, 240, 18, 18, 16000000 /*18 bpp, 18 bus*/
|
|
#endif
|
|
};
|
|
|
|
|
|
static volatile unsigned char *slcd_palette;
|
|
static volatile unsigned char *slcd_frame;
|
|
|
|
//extern struct display fb_display[MAX_NR_CONSOLES];
|
|
static irqreturn_t slcd_dma_irq(int irq, void *dev_id);
|
|
|
|
|
|
static void Mcupanel_RegSet(UINT32 cmd, UINT32 data)
|
|
{
|
|
switch (jzfb.bus) {
|
|
case 8:
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff);
|
|
break;
|
|
case 9:
|
|
data = ((data & 0xff) << 1) | ((data & 0xff00) << 2);
|
|
data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_DATA | data;
|
|
break;
|
|
case 16:
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff);
|
|
break;
|
|
case 18:
|
|
cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2);
|
|
data = ((data & 0xff) << 1) | ((data & 0xff00) << 2);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd;
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_DATA | ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc);
|
|
break;
|
|
default:
|
|
printk("Don't support %d bit Bus\n", jzfb.bus );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Sent a command withou data */
|
|
static void Mcupanel_Command(UINT32 cmd) {
|
|
switch (jzfb.bus) {
|
|
case 8:
|
|
case 9:
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8);
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0);
|
|
break;
|
|
case 16:
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff);
|
|
break;
|
|
case 18:
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1);
|
|
break;
|
|
default:
|
|
printk("Don't support %d bit Bus\n", jzfb.bus );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Set the start address of screen, for example (0, 0) */
|
|
#ifdef CONFIG_JZ_SLCD_LGDP4551
|
|
static void Mcupanel_SetAddr(UINT16 x, UINT16 y)
|
|
{
|
|
Mcupanel_RegSet(0x20,x) ;
|
|
udelay(1);
|
|
Mcupanel_RegSet(0x21,y) ;
|
|
udelay(1);
|
|
Mcupanel_Command(0x22);
|
|
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_JZ_SLCD_SPFD5420A
|
|
void Mcupanel_SetAddr(u32 x, u32 y) //u32
|
|
{
|
|
Mcupanel_RegSet(0x200,x) ;
|
|
udelay(1);
|
|
Mcupanel_RegSet(0x201,y) ;
|
|
udelay(1);
|
|
Mcupanel_Command(0x202);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
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:
|
|
/* 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 *)slcd_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 *info, unsigned int cmd, unsigned long arg )
|
|
{
|
|
int ret = 0;
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
switch (cmd) {
|
|
case FBIOSETBACKLIGHT:
|
|
__slcd_set_backlight_level(arg); /* We support 8 levels here. */
|
|
break;
|
|
case FBIODISPON:
|
|
__slcd_display_on();
|
|
break;
|
|
case FBIODISPOFF:
|
|
__slcd_display_off();
|
|
break;
|
|
case FBIO_REFRESH_ALWAYS:
|
|
dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd);
|
|
if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK)
|
|
printk("The Smart LCD refreshes automatically. Option is omitted!\n");
|
|
else {
|
|
dprintk("OPEN DMAC_DCMD_LINK \n");
|
|
slcd_frame_desc.dcmd &= ~DMAC_DCMD_TIE;
|
|
slcd_frame_desc.dcmd |= DMAC_DCMD_LINK;
|
|
dma_cache_wback((unsigned long)(&slcd_frame_desc), 16);
|
|
REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_TIE;
|
|
__dmac_channel_set_doorbell(dma_chan);
|
|
}
|
|
break;
|
|
case FBIO_REFRESH_EVENTS:
|
|
dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd);
|
|
if (!(slcd_frame_desc.dcmd & DMAC_DCMD_LINK))
|
|
printk("The Smart LCD is refreshed by envents. Option is omitted!\n");
|
|
else {
|
|
non_link_desp = 1;
|
|
REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE;
|
|
REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK;
|
|
}
|
|
break;
|
|
case FBIO_DO_REFRESH:
|
|
|
|
dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd);
|
|
if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK)
|
|
printk("The Smart LCD can refresh automatically. Option is omitted!\n");
|
|
else {
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
__dmac_channel_set_doorbell(dma_chan);
|
|
}
|
|
break;
|
|
case FBIO_SET_REG:
|
|
if (copy_from_user(®_buf, argp, sizeof(reg_buf)))
|
|
return -EFAULT;
|
|
is_set_reg = 1;
|
|
REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE;
|
|
REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK;
|
|
break;
|
|
default:
|
|
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");
|
|
printk("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:
|
|
/* Turn on panel */
|
|
break;
|
|
|
|
case FB_BLANK_NORMAL:
|
|
case FB_BLANK_VSYNC_SUSPEND:
|
|
case FB_BLANK_HSYNC_SUSPEND:
|
|
case FB_BLANK_POWERDOWN:
|
|
/* Turn off panel */
|
|
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);
|
|
// slcd_frame_desc->databuf += (cfb->fb.fix.line_length * dy);
|
|
// slcd_frame_desc->dsadr += (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,
|
|
};
|
|
|
|
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;
|
|
//struct display *display;
|
|
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;
|
|
|
|
//display = fb_display + con;
|
|
|
|
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, 256 */
|
|
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;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
jzslcd_info = cfb;
|
|
|
|
memset(cfb, 0, sizeof(struct lcd_cfb_info) );
|
|
|
|
cfb->currcon = -1;
|
|
|
|
|
|
strcpy(cfb->fb.fix.id, "jz-slcd");
|
|
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;
|
|
|
|
t = jzfb.bpp;
|
|
if (jzfb.bpp == 15)
|
|
t = 16;
|
|
if (jzfb.bpp == 18 || jzfb.bpp == 24)
|
|
t = 32;
|
|
needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h;
|
|
|
|
for (page_shift = 0; page_shift < 12; page_shift++)
|
|
if ((PAGE_SIZE << page_shift) >= needroom)
|
|
break;
|
|
|
|
slcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0);
|
|
slcd_frame = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift);
|
|
if ((!slcd_palette) || (!slcd_frame))
|
|
return -ENOMEM;
|
|
|
|
memset((void *)slcd_palette, 0, PAGE_SIZE);
|
|
memset((void *)slcd_frame, 0, PAGE_SIZE << page_shift);
|
|
|
|
map = virt_to_page(slcd_palette);
|
|
set_bit(PG_reserved, &map->flags);
|
|
|
|
for (tmp=(unsigned char *)slcd_frame;
|
|
tmp < slcd_frame + (PAGE_SIZE << page_shift);
|
|
tmp += PAGE_SIZE) {
|
|
map = virt_to_page(tmp);
|
|
set_bit(PG_reserved, &map->flags);
|
|
}
|
|
|
|
cfb->fb.fix.smem_start = virt_to_phys((void *)slcd_frame);
|
|
|
|
cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift);
|
|
|
|
cfb->fb.screen_base =
|
|
(unsigned char *)(((unsigned int)slcd_frame & 0x1fffffff) | 0xa0000000);
|
|
|
|
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;
|
|
|
|
t = jzfb.bpp;
|
|
if (jzfb.bpp == 18 || jzfb.bpp == 24)
|
|
t = 32;
|
|
if (jzfb.bpp == 15)
|
|
t = 16;
|
|
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 (slcd_palette) {
|
|
map = virt_to_page(slcd_palette);
|
|
clear_bit(PG_reserved, &map->flags);
|
|
free_pages((int)slcd_palette, 0);
|
|
}
|
|
|
|
if (slcd_frame) {
|
|
|
|
for (tmp=(unsigned char *)slcd_frame;
|
|
tmp < slcd_frame + (PAGE_SIZE << page_shift);
|
|
tmp += PAGE_SIZE) {
|
|
map = virt_to_page(tmp);
|
|
clear_bit(PG_reserved, &map->flags);
|
|
}
|
|
|
|
free_pages((int)slcd_frame, page_shift);
|
|
}
|
|
}
|
|
|
|
static void slcd_descriptor_init(void)
|
|
{
|
|
int i;
|
|
int frm_size, pal_size;
|
|
unsigned int next;
|
|
unsigned int slcd_frame_src_phys_addr, slcd_palette_src_phys_addr, slcd_dma_dst_phys_addr;
|
|
|
|
i = jzfb.bpp;
|
|
if (i == 18 || i == 24)
|
|
i = 32;
|
|
if (i == 15)
|
|
i = 16;
|
|
|
|
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;
|
|
}
|
|
|
|
frm_size = jzfb.w * jzfb.h * jzfb.bpp / 8;
|
|
|
|
/*Offset of next descriptor*/
|
|
slcd_frame_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_frame_desc));
|
|
slcd_palette_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_palette_desc));
|
|
|
|
/*Soure address and Target address*/
|
|
slcd_palette_src_phys_addr = (unsigned int)virt_to_phys(slcd_palette);
|
|
slcd_frame_src_phys_addr = (unsigned int)virt_to_phys(slcd_frame);
|
|
slcd_dma_dst_phys_addr = (unsigned int)CPHYSADDR(SLCD_FIFO);
|
|
next = slcd_frame_desc_phys_addr >> 4;
|
|
|
|
/* Prepare Palette Descriptor */
|
|
slcd_palette_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32
|
|
| DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V
|
|
| DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK;
|
|
switch (slcd_palette_desc.dcmd & DMAC_DCMD_DS_MASK) {
|
|
case DMAC_DCMD_DS_32BYTE:
|
|
pal_size /= 32;
|
|
break;
|
|
case DMAC_DCMD_DS_16BYTE:
|
|
pal_size /= 16;
|
|
break;
|
|
case DMAC_DCMD_DS_32BIT:
|
|
pal_size /= 4;
|
|
break;
|
|
case DMAC_DCMD_DS_16BIT:
|
|
pal_size /= 2;
|
|
break;
|
|
case DMAC_DCMD_DS_8BIT:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
slcd_palette_desc.dsadr = (unsigned int)virt_to_phys(slcd_palette); /* DMA source address */
|
|
slcd_palette_desc.dtadr = (unsigned int)CPHYSADDR(SLCD_FIFO); /* DMA target address */
|
|
slcd_palette_desc.ddadr = (volatile unsigned int)((next << 24) | (pal_size & 0xffffff)); /* offset and size*/
|
|
dma_cache_wback((unsigned long)(&slcd_palette_desc), 16);
|
|
|
|
/*Prepare Frame Descriptor in memory*/
|
|
switch (jzfb.bpp) {
|
|
case 8 ... 16:
|
|
slcd_frame_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32
|
|
| DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V
|
|
| DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK;
|
|
break;
|
|
|
|
case 17 ... 32:
|
|
slcd_frame_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32
|
|
| DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V
|
|
| DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK;
|
|
break;
|
|
}
|
|
switch (slcd_frame_desc.dcmd & DMAC_DCMD_DS_MASK) {
|
|
case DMAC_DCMD_DS_32BYTE:
|
|
frm_size /= 32;
|
|
break;
|
|
case DMAC_DCMD_DS_16BYTE:
|
|
frm_size /= 16;
|
|
break;
|
|
case DMAC_DCMD_DS_32BIT:
|
|
frm_size /= 4;
|
|
break;
|
|
case DMAC_DCMD_DS_16BIT:
|
|
frm_size /= 2;
|
|
break;
|
|
case DMAC_DCMD_DS_8BIT:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
slcd_frame_desc.dsadr = slcd_frame_src_phys_addr; /* DMA source address */
|
|
slcd_frame_desc.dtadr = slcd_dma_dst_phys_addr; /* DMA target address */
|
|
slcd_frame_desc.ddadr = (volatile unsigned int)((next << 24) | (frm_size & 0xffffff)); /* offset and size*/
|
|
dma_cache_wback((unsigned long)(&slcd_frame_desc), 16);
|
|
}
|
|
|
|
void slcd_hw_init(void)
|
|
{
|
|
unsigned int val, pclk;
|
|
int pll_div;
|
|
|
|
REG_LCD_CFG &= ~LCD_CFG_LCDPIN_MASK;
|
|
REG_LCD_CFG |= LCD_CFG_LCDPIN_SLCD;
|
|
|
|
if ((jzfb.bpp == 18) | (jzfb.bpp == 24))
|
|
jzfb.bpp = 32;
|
|
|
|
/* Configure SLCD module for initialize smart lcd registers*/
|
|
switch (jzfb.bus) {
|
|
case 8:
|
|
REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2
|
|
| SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW
|
|
| SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING
|
|
| SLCD_CFG_TYPE_PARALLEL;
|
|
__gpio_as_slcd_8bit();
|
|
break;
|
|
case 9:
|
|
REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2
|
|
| SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW
|
|
| SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING
|
|
| SLCD_CFG_TYPE_PARALLEL;
|
|
__gpio_as_slcd_9bit();
|
|
break;
|
|
case 16:
|
|
REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_16
|
|
| SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW
|
|
| SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING
|
|
| SLCD_CFG_TYPE_PARALLEL;
|
|
__gpio_as_slcd_16bit();
|
|
break;
|
|
case 18:
|
|
REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_18
|
|
| SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW
|
|
| SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING
|
|
| SLCD_CFG_TYPE_PARALLEL;
|
|
__gpio_as_slcd_18bit();
|
|
break;
|
|
default:
|
|
printk("Error: Don't support BUS %d!\n", jzfb.bus);
|
|
break;
|
|
}
|
|
|
|
REG_SLCD_CTRL = SLCD_CTRL_DMA_EN;
|
|
__cpm_stop_lcd();
|
|
pclk = jzfb.pclk;
|
|
pll_div = ( REG_CPM_CPCCR & CPM_CPCCR_PCS ); /* clock source,0:pllout/2 1: pllout */
|
|
pll_div = pll_div ? 1 : 2 ;
|
|
val = ( __cpm_get_pllout()/pll_div ) / pclk;
|
|
val--;
|
|
if ( val > 0x1ff ) {
|
|
printk("CPM_LPCDR too large, set it to 0x1ff\n");
|
|
val = 0x1ff;
|
|
}
|
|
__cpm_set_pixdiv(val);
|
|
|
|
REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */
|
|
|
|
jz_clocks.pixclk = __cpm_get_pixclk();
|
|
jz_clocks.lcdclk = __cpm_get_lcdclk();
|
|
printk("SLCDC: PixClock:%d LcdClock:%d\n",
|
|
jz_clocks.pixclk, jz_clocks.lcdclk);
|
|
|
|
__cpm_start_lcd();
|
|
udelay(1000);
|
|
__slcd_display_pin_init();
|
|
__slcd_special_on();
|
|
|
|
/* Configure SLCD module for transfer data to smart lcd GRAM*/
|
|
switch (jzfb.bus) {
|
|
case 8:
|
|
switch (jzfb.bpp) {
|
|
case 8:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1;
|
|
break;
|
|
case 15:
|
|
case 16:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2;
|
|
break;
|
|
case 17 ... 32:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3;
|
|
break;
|
|
default:
|
|
printk("The BPP %d is not supported\n", jzfb.bpp);
|
|
break;
|
|
}
|
|
break;
|
|
case 9:
|
|
switch (jzfb.bpp) {
|
|
case 8:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1;
|
|
break;
|
|
case 15 ... 16:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2;
|
|
break;
|
|
case 17 ... 32:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_9_x2;
|
|
break;
|
|
default:
|
|
printk("The BPP %d is not supported\n", jzfb.bpp);
|
|
break;
|
|
}
|
|
break;
|
|
case 16:
|
|
switch (jzfb.bpp) {
|
|
case 8:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1;
|
|
break;
|
|
case 15 ... 16:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16;
|
|
break;
|
|
case 17 ... 32:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3;
|
|
break;
|
|
default:
|
|
printk("The BPP %d is not supported\n", jzfb.bpp);
|
|
break;
|
|
}
|
|
break;
|
|
case 18:
|
|
switch (jzfb.bpp) {
|
|
case 8:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1;
|
|
break;
|
|
case 15:
|
|
case 16:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16;
|
|
break;
|
|
case 17 ... 32:
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_18;
|
|
break;
|
|
default:
|
|
printk("The BPP %d is not supported\n", jzfb.bpp);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
printk("Error: The BUS %d is not supported\n", jzfb.bus);
|
|
break;
|
|
}
|
|
dprintk("SLCD_CFG=0x%x\n", REG_SLCD_CFG);
|
|
}
|
|
|
|
static irqreturn_t slcd_dma_irq(int irq, void *dev_id)
|
|
{
|
|
|
|
if (__dmac_channel_transmit_halt_detected(dma_chan)) {
|
|
dprintk("DMA HALT\n");
|
|
__dmac_channel_clear_transmit_halt(dma_chan);
|
|
}
|
|
|
|
if (__dmac_channel_address_error_detected(dma_chan)) {
|
|
dprintk("DMA ADDR ERROR\n");
|
|
__dmac_channel_clear_address_error(dma_chan);
|
|
}
|
|
|
|
if (__dmac_channel_descriptor_invalid_detected(dma_chan)) {
|
|
dprintk("DMA DESC INVALID\n");
|
|
__dmac_channel_clear_descriptor_invalid(dma_chan);
|
|
}
|
|
|
|
if (__dmac_channel_count_terminated_detected(dma_chan)) {
|
|
dprintk("DMA CT\n");
|
|
__dmac_channel_clear_count_terminated(dma_chan);
|
|
if(is_set_reg){
|
|
printk("Close DMAC_DCMD_LINK \n");
|
|
REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK;
|
|
}
|
|
if (non_link_desp) {
|
|
printk("Close DMAC_DCMD_LINK \n");
|
|
/*Set to Non-Link Descriptor*/
|
|
REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK;
|
|
}
|
|
}
|
|
|
|
if (__dmac_channel_transmit_end_detected(dma_chan)) {
|
|
printk("DMA TT\n");
|
|
__dmac_channel_clear_transmit_end(dma_chan);
|
|
if (non_link_desp) {
|
|
slcd_frame_desc.dcmd |= DMAC_DCMD_TIE;
|
|
slcd_frame_desc.dcmd &= ~DMAC_DCMD_LINK;
|
|
dma_cache_wback((unsigned long)(&slcd_frame_desc), 16);
|
|
non_link_desp = 0;
|
|
}
|
|
if (is_set_reg) {
|
|
is_set_reg = 0;
|
|
while (REG_SLCD_STATE & SLCD_STATE_BUSY);
|
|
REG_DMAC_DMACR &= ~DMAC_DMACR_DMAE; /* disable DMA */
|
|
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
|
|
REG_SLCD_CTRL = 0;
|
|
|
|
/*
|
|
*add operation here
|
|
*/
|
|
Mcupanel_RegSet(reg_buf.cmd, reg_buf.data);
|
|
Mcupanel_Command(0x0022);/*Write Data to GRAM */
|
|
mdelay(100);
|
|
REG_SLCD_CTRL = SLCD_CTRL_DMA_EN;
|
|
REG_DMAC_DMACR = DMAC_DMACR_DMAE;
|
|
REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN;
|
|
__dmac_channel_set_doorbell(dma_chan);
|
|
}
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int slcd_dma_init(void)
|
|
{
|
|
/* Request DMA channel and setup irq handler */
|
|
dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", slcd_dma_irq, 0, NULL);
|
|
if (dma_chan < 0) {
|
|
printk("Request DMA Failed\n");
|
|
return -1;
|
|
}
|
|
printk("DMA channel %d is requested by SLCD!\n", dma_chan);
|
|
|
|
/*Init the SLCD DMA and Enable*/
|
|
REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_SLCD;
|
|
REG_DMAC_DMACR = DMAC_DMACR_DMAE;
|
|
REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /*Descriptor Transfer*/
|
|
|
|
if (jzfb.bpp <= 8)
|
|
REG_DMAC_DDA(dma_chan) = slcd_palette_desc_phys_addr;
|
|
else
|
|
REG_DMAC_DDA(dma_chan) = slcd_frame_desc_phys_addr;
|
|
|
|
/* DMA doorbell set -- start DMA now ... */
|
|
__dmac_channel_set_doorbell(dma_chan);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
/*
|
|
* Suspend the LCDC.
|
|
*/
|
|
static int jzfb_suspend(void)
|
|
{
|
|
|
|
__slcd_close_backlight();
|
|
__dmac_disable_channel(dma_chan);
|
|
__slcd_dma_disable(); /* Quick Disable */
|
|
__slcd_special_off();
|
|
__cpm_stop_lcd();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Resume the LCDC.
|
|
*/
|
|
|
|
static int jzfb_resume(void)
|
|
{
|
|
__cpm_start_lcd();
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
switch (jzfb.bpp) {
|
|
case 8:
|
|
/* DATA 8-bit once*/
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1;
|
|
break;
|
|
case 15:
|
|
case 16:
|
|
case 18:
|
|
case 24:
|
|
case 32:
|
|
/* DATA 8-bit twice*/
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2;
|
|
break;
|
|
default:
|
|
REG_SLCD_CFG = SLCD_CFG_DWIDTH_8_x2;
|
|
break;
|
|
}
|
|
__slcd_display_pin_init();
|
|
__slcd_special_on();
|
|
|
|
if (jzfb.bpp == 32) {
|
|
/* DATA 8-bit three time*/
|
|
REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK;
|
|
REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3;
|
|
}
|
|
__slcd_dma_enable();
|
|
udelay(100);
|
|
__dmac_enable_channel(dma_chan);
|
|
__dmac_channel_set_doorbell(dma_chan);
|
|
mdelay(200);
|
|
__slcd_set_backlight_level(80);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Power management hook. Note that we won't be called from IRQ context,
|
|
* unlike the blank functions above, so we may sleep.
|
|
*/
|
|
static int jzslcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
|
|
{
|
|
int ret;
|
|
struct lcd_cfb_info *cfb = pm_dev->data;
|
|
|
|
if (!cfb) return -EINVAL;
|
|
|
|
switch (req) {
|
|
case PM_SUSPEND:
|
|
ret = jzfb_suspend();
|
|
break;
|
|
|
|
case PM_RESUME:
|
|
ret = jzfb_resume();
|
|
break;
|
|
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
#else
|
|
#define jzfb_suspend NULL
|
|
#define jzfb_resume NULL
|
|
#endif /* CONFIG_PM */
|
|
static int __init jzslcd_fb_init(void)
|
|
{
|
|
|
|
struct lcd_cfb_info *cfb;
|
|
int err = 0;
|
|
|
|
/*the parameters of slcd*/
|
|
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);
|
|
|
|
slcd_hw_init();
|
|
|
|
err = register_framebuffer(&cfb->fb);
|
|
if (err < 0) {
|
|
printk("jzslcd_fb_init(): slcd 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);
|
|
|
|
slcd_descriptor_init();
|
|
err = slcd_dma_init();
|
|
if (err != 0) {
|
|
printk("SLCD Init DMA Fail!\n");
|
|
return err;
|
|
}
|
|
mdelay(100);
|
|
__slcd_set_backlight_level(80);
|
|
|
|
#ifdef CONFIG_PM
|
|
/*
|
|
* Note that the console registers this as well, but we want to
|
|
* power down the display prior to sleeping.
|
|
*/
|
|
//struct pm_dev __deprecated *pm_register(pm_dev_t type, unsigned long id, pm_callback callback);
|
|
|
|
cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzslcd_pm_callback);
|
|
if (cfb->pm)
|
|
cfb->pm->data = cfb;
|
|
|
|
#endif
|
|
return 0;
|
|
|
|
failed:
|
|
jzfb_unmap_smem(cfb);
|
|
jzfb_free_fb_info(cfb);
|
|
|
|
return err;
|
|
}
|
|
|
|
#if 0
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static struct device_driver jzfb_driver = {
|
|
.name = "jz-slcd",
|
|
.bus = &platform_bus_type,
|
|
.probe = jzfb_probe,
|
|
.remove = jzfb_remove,
|
|
.suspend = jzfb_suspend,
|
|
.resume = jzfb_resume,
|
|
};
|
|
#endif
|
|
|
|
static void __exit jzslcd_fb_cleanup(void)
|
|
{
|
|
//driver_unregister(&jzfb_driver);
|
|
//jzfb_remove();
|
|
}
|
|
|
|
module_init(jzslcd_fb_init);
|
|
module_exit(jzslcd_fb_cleanup);
|
|
|
|
MODULE_DESCRIPTION("JzSOC SLCD Controller driver");
|
|
MODULE_LICENSE("GPL");
|