/* drivers/video/s1d13xxxfb.c
*
* (c) 2004 Simtec Electronics
* (c) 2005 Thibaut VARENE <varenet@parisc-linux.org>
* (c) 2009 Kristoffer Ericson <kristoffer.ericson@gmail.com>
*
* Driver for Epson S1D13xxx series framebuffer chips
*
* Adapted from
* linux/drivers/video/skeletonfb.c
* linux/drivers/video/epson1355fb.c
* linux/drivers/video/epson/s1d13xxxfb.c (2.4 driver by Epson)
*
* TODO: - handle dual screen display (CRT and LCD at the same time).
* - check_var(), mode change, etc.
* - probably not SMP safe :)
* - support all bitblt operations on all cards
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/fb.h>
#include <linux/spinlock_types.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <video/s1d13xxxfb.h>
#define PFX "s1d13xxxfb: "
#define BLIT "s1d13xxxfb_bitblt: "
/*
* set this to enable debugging on general functions
*/
#if 0
#define dbg(fmt, args...) do { printk(KERN_INFO fmt, ## args); } while(0)
#else
#define dbg(fmt, args...) do { } while (0)
#endif
/*
* set this to enable debugging on 2D acceleration
*/
#if 0
#define dbg_blit(fmt, args...) do { printk(KERN_INFO BLIT fmt, ## args); } while (0)
#else
#define dbg_blit(fmt, args...) do { } while (0)
#endif
/*
* we make sure only one bitblt operation is running
*/
static DEFINE_SPINLOCK(s1d13xxxfb_bitblt_lock);
/*
* list of card production ids
*/
static const int s1d13xxxfb_prod_ids[] = {
S1D13505_PROD_ID,
S1D13506_PROD_ID,
S1D13806_PROD_ID,
};
/*
* List of card strings
*/
static const char *s1d13xxxfb_prod_names[] = {
"S1D13505",
"S1D13506",
"S1D13806",
};
/*
* here we define the default struct fb_fix_screeninfo
*/
static struct fb_fix_screeninfo __devinitdata s1d13xxxfb_fix = {
.id = S1D_FBID,
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_PSEUDOCOLOR,
.xpanstep = 0,
.ypanstep = 1,
.ywrapstep = 0,
.accel = FB_ACCEL_NONE,
};
static inline u8
s1d13xxxfb_readreg(struct s1d13xxxfb_par *par, u16 regno)
{
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3)
regno=((regno & 1) ? (regno & ~1L) : (regno + 1));
#endif
return readb(par->regs + regno);
}
static inline void
s1d13xxxfb_writereg(struct s1d13xxxfb_par *par, u16 regno, u8 value)
{
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3)
regno=((regno & 1) ? (regno & ~1L) : (regno + 1));
#endif
writeb(value, par->regs + regno);
}
static inline void
s1d13xxxfb_runinit(struct s1d13xxxfb_par *par,
const struct s1d13xxxfb_regval *initregs,
const unsigned int size)
{
int i;
for (i = 0; i < size; i++) {
if ((initregs[i].addr == S1DREG_DELAYOFF) ||
(initregs[i].addr == S1DREG_DELAYON))
mdelay((int)initregs[i].value);
else {
s1d13xxxfb_writereg(par, initregs[i].addr, initregs[i].value);
}
}
/* make sure the hardware can cope with us */
mdelay(1);
}
static inline void
lcd_enable(struct s1d13xxxfb_par *par, int enable)
{
u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
if (enable)
mode |= 0x01;
else
mode &= ~0x01;
s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
}
static inline void
crt_enable(struct s1d13xxxfb_par *par, int enable)
{
u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
if (enable)
mode |= 0x02;
else
mode &= ~0x02;
s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
}
/*************************************************************
framebuffer control functions
*************************************************************/
static inline void
s1d13xxxfb_setup_pseudocolour(struct fb_info *info)
{
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
info->var.red.length = 4;
info->var.green<