/* linux/drivers/video/s3c2410fb.c
* Copyright (c) 2004,2005 Arnaud Patard
* Copyright (c) 2004-2008 Ben Dooks
*
* S3C2410 LCD Framebuffer Driver
*
* 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.
*
* Driver based on skeletonfb.c, sa1100fb.c and others.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/fb.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include "s3c2410fb.h"
/* Debugging stuff */
#ifdef CONFIG_FB_S3C2410_DEBUG
static int debug = 1;
#else
static int debug = 0;
#endif
#define dprintk(msg...) if (debug) { printk(KERN_DEBUG "s3c2410fb: " msg); }
/* useful functions */
static int is_s3c2412(struct s3c2410fb_info *fbi)
{
return (fbi->drv_type == DRV_S3C2412);
}
/* s3c2410fb_set_lcdaddr
*
* initialise lcd controller address pointers
*/
static void s3c2410fb_set_lcdaddr(struct fb_info *info)
{
unsigned long saddr1, saddr2, saddr3;
struct s3c2410fb_info *fbi = info->par;
void __iomem *regs = fbi->io;
saddr1 = info->fix.smem_start >> 1;
saddr2 = info->fix.smem_start;
saddr2 += info->fix.line_length * info->var.yres;
saddr2 >>= 1;
saddr3 = S3C2410_OFFSIZE(0) |
S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
writel(saddr1, regs + S3C2410_LCDSADDR1);
writel(saddr2, regs + S3C2410_LCDSADDR2);
writel(saddr3, regs + S3C2410_LCDSADDR3);
}
/* s3c2410fb_calc_pixclk()
*
* calculate divisor for clk->pixclk
*/
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
unsigned long pixclk)
{
unsigned long clk = clk_get_rate(fbi->clk);
unsigned long long div;
/* pixclk is in picoseconds, our clock is in Hz
*
* Hz -> picoseconds is / 10^-12
*/
div = (unsigned long long)clk * pixclk;
div >>= 12; /* div / 2^12 */
do_div(div, 625 * 625UL * 625); /* div / 5^12 */
dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
return div;
}
/*
* s3c2410fb_check_var():
* Get the video params out of 'var'. If a value doesn't fit, round it up,
* if it's too big, return -EINVAL.
*
*/
static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct s3c2410fb_info *fbi = info->par;
struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
struct s3c2410fb_display *display = NULL;
struct s3c2410fb_display *default_display = mach_info->displays +
mach_info->default_display;
int type = default_display->type;
unsigned i;
dprintk("check_var(var=%p, info=%p)\n", var, info);
/* validate x/y resolution */
/* choose default mode if possible */
if (var->yres == default_display->yres &&
var->xres == default_display->xres &&
var->bits_per_pixel == default_display->bpp)
display = default_display;
else
for (i = 0; i < mach_info->num_displays; i++)
if (type == mach_info->displays[i].type &&
var->yres == mach_info->displays[i].yres &&
var->xres == mach_info->displays[i].xres &&
var->bits_per_pixel == mach_info-><