2009-03-31 15:25:39 -07:00
|
|
|
/* linux/drivers/video/s3c-fb.c
|
|
|
|
*
|
|
|
|
* Copyright 2008 Openmoko Inc.
|
2010-08-10 18:02:33 -07:00
|
|
|
* Copyright 2008-2010 Simtec Electronics
|
2009-03-31 15:25:39 -07:00
|
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
|
|
* http://armlinux.simtec.co.uk/
|
|
|
|
*
|
|
|
|
* Samsung SoC Framebuffer driver
|
|
|
|
*
|
|
|
|
* 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
|
2010-08-10 18:02:34 -07:00
|
|
|
* published by the Free Software FoundatIon.
|
2009-03-31 15:25:39 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 01:04:11 -07:00
|
|
|
#include <linux/slab.h>
|
2009-03-31 15:25:39 -07:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/fb.h>
|
|
|
|
#include <linux/io.h>
|
2010-08-10 18:02:38 -07:00
|
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <linux/interrupt.h>
|
2010-12-17 00:45:46 -07:00
|
|
|
#include <linux/pm_runtime.h>
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
#include <mach/map.h>
|
2010-08-10 18:02:34 -07:00
|
|
|
#include <plat/regs-fb-v4.h>
|
2009-03-31 15:25:39 -07:00
|
|
|
#include <plat/fb.h>
|
|
|
|
|
|
|
|
/* This driver will export a number of framebuffer interfaces depending
|
|
|
|
* on the configuration passed in via the platform data. Each fb instance
|
|
|
|
* maps to a hardware window. Currently there is no support for runtime
|
|
|
|
* setting of the alpha-blending functions that each window has, so only
|
|
|
|
* window 0 is actually useful.
|
|
|
|
*
|
|
|
|
* Window 0 is treated specially, it is used for the basis of the LCD
|
|
|
|
* output timings and as the control for the output power-down state.
|
|
|
|
*/
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
/* note, the previous use of <mach/regs-fb.h> to get platform specific data
|
|
|
|
* has been replaced by using the platform device name to pick the correct
|
|
|
|
* configuration data for the system.
|
2009-03-31 15:25:39 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE
|
|
|
|
#undef writel
|
|
|
|
#define writel(v, r) do { \
|
|
|
|
printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \
|
2011-04-01 00:17:27 -07:00
|
|
|
__raw_writel(v, r); } while (0)
|
2009-03-31 15:25:39 -07:00
|
|
|
#endif /* FB_S3C_DEBUG_REGWRITE */
|
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
/* irq_flags bits */
|
|
|
|
#define S3C_FB_VSYNC_IRQ_EN 0
|
|
|
|
|
|
|
|
#define VSYNC_TIMEOUT_MSEC 50
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
struct s3c_fb;
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
#define VALID_BPP(x) (1 << ((x) - 1))
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride))
|
|
|
|
#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00)
|
|
|
|
#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04)
|
|
|
|
#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08)
|
|
|
|
#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C)
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
/**
|
|
|
|
* struct s3c_fb_variant - fb variant information
|
2010-08-10 18:02:34 -07:00
|
|
|
* @is_2443: Set if S3C2443/S3C2416 style hardware.
|
2010-08-10 18:02:33 -07:00
|
|
|
* @nr_windows: The number of windows.
|
2010-08-10 18:02:34 -07:00
|
|
|
* @vidtcon: The base for the VIDTCONx registers
|
|
|
|
* @wincon: The base for the WINxCON registers.
|
|
|
|
* @winmap: The base for the WINxMAP registers.
|
|
|
|
* @keycon: The abse for the WxKEYCON registers.
|
|
|
|
* @buf_start: Offset of buffer start registers.
|
|
|
|
* @buf_size: Offset of buffer size registers.
|
|
|
|
* @buf_end: Offset of buffer end registers.
|
|
|
|
* @osd: The base for the OSD registers.
|
2010-08-10 18:02:33 -07:00
|
|
|
* @palette: Address of palette memory, or 0 if none.
|
2010-08-10 18:02:38 -07:00
|
|
|
* @has_prtcon: Set if has PRTCON register.
|
2010-08-10 18:02:40 -07:00
|
|
|
* @has_shadowcon: Set if has SHADOWCON register.
|
2011-08-21 20:16:04 -07:00
|
|
|
* @has_clksel: Set if VIDCON0 register has CLKSEL bit.
|
2010-08-10 18:02:33 -07:00
|
|
|
*/
|
|
|
|
struct s3c_fb_variant {
|
2010-08-10 18:02:34 -07:00
|
|
|
unsigned int is_2443:1;
|
2010-08-10 18:02:33 -07:00
|
|
|
unsigned short nr_windows;
|
2010-08-10 18:02:34 -07:00
|
|
|
unsigned short vidtcon;
|
|
|
|
unsigned short wincon;
|
|
|
|
unsigned short winmap;
|
|
|
|
unsigned short keycon;
|
|
|
|
unsigned short buf_start;
|
|
|
|
unsigned short buf_end;
|
|
|
|
unsigned short buf_size;
|
|
|
|
unsigned short osd;
|
|
|
|
unsigned short osd_stride;
|
2010-08-10 18:02:33 -07:00
|
|
|
unsigned short palette[S3C_FB_MAX_WIN];
|
2010-08-10 18:02:38 -07:00
|
|
|
|
|
|
|
unsigned int has_prtcon:1;
|
2010-08-10 18:02:40 -07:00
|
|
|
unsigned int has_shadowcon:1;
|
2011-08-21 20:16:04 -07:00
|
|
|
unsigned int has_clksel:1;
|
2010-08-10 18:02:33 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct s3c_fb_win_variant
|
|
|
|
* @has_osd_c: Set if has OSD C register.
|
|
|
|
* @has_osd_d: Set if has OSD D register.
|
2010-08-10 18:02:40 -07:00
|
|
|
* @has_osd_alpha: Set if can change alpha transparency for a window.
|
2010-08-10 18:02:33 -07:00
|
|
|
* @palette_sz: Size of palette in entries.
|
|
|
|
* @palette_16bpp: Set if palette is 16bits wide.
|
2010-08-10 18:02:40 -07:00
|
|
|
* @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate
|
|
|
|
* register is located at the given offset from OSD_BASE.
|
2010-08-10 18:02:33 -07:00
|
|
|
* @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel.
|
|
|
|
*
|
|
|
|
* valid_bpp bit x is set if (x+1)BPP is supported.
|
|
|
|
*/
|
|
|
|
struct s3c_fb_win_variant {
|
|
|
|
unsigned int has_osd_c:1;
|
|
|
|
unsigned int has_osd_d:1;
|
2010-08-10 18:02:40 -07:00
|
|
|
unsigned int has_osd_alpha:1;
|
2010-08-10 18:02:33 -07:00
|
|
|
unsigned int palette_16bpp:1;
|
2010-08-10 18:02:40 -07:00
|
|
|
unsigned short osd_size_off;
|
2010-08-10 18:02:33 -07:00
|
|
|
unsigned short palette_sz;
|
|
|
|
u32 valid_bpp;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct s3c_fb_driverdata - per-device type driver data for init time.
|
|
|
|
* @variant: The variant information for this driver.
|
|
|
|
* @win: The window information for each window.
|
|
|
|
*/
|
|
|
|
struct s3c_fb_driverdata {
|
|
|
|
struct s3c_fb_variant variant;
|
|
|
|
struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN];
|
|
|
|
};
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
/**
|
|
|
|
* struct s3c_fb_palette - palette information
|
|
|
|
* @r: Red bitfield.
|
|
|
|
* @g: Green bitfield.
|
|
|
|
* @b: Blue bitfield.
|
|
|
|
* @a: Alpha bitfield.
|
|
|
|
*/
|
|
|
|
struct s3c_fb_palette {
|
|
|
|
struct fb_bitfield r;
|
|
|
|
struct fb_bitfield g;
|
|
|
|
struct fb_bitfield b;
|
|
|
|
struct fb_bitfield a;
|
|
|
|
};
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
/**
|
|
|
|
* struct s3c_fb_win - per window private data for each framebuffer.
|
|
|
|
* @windata: The platform data supplied for the window configuration.
|
|
|
|
* @parent: The hardware that this window is part of.
|
|
|
|
* @fbinfo: Pointer pack to the framebuffer info for this window.
|
2010-08-10 18:02:33 -07:00
|
|
|
* @varint: The variant information for this window.
|
2009-03-31 15:25:39 -07:00
|
|
|
* @palette_buffer: Buffer/cache to hold palette entries.
|
|
|
|
* @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
|
|
|
|
* @index: The window number of this window.
|
|
|
|
* @palette: The bitfields for changing r/g/b into a hardware palette entry.
|
|
|
|
*/
|
|
|
|
struct s3c_fb_win {
|
|
|
|
struct s3c_fb_pd_win *windata;
|
|
|
|
struct s3c_fb *parent;
|
|
|
|
struct fb_info *fbinfo;
|
|
|
|
struct s3c_fb_palette palette;
|
2010-08-10 18:02:33 -07:00
|
|
|
struct s3c_fb_win_variant variant;
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
u32 *palette_buffer;
|
|
|
|
u32 pseudo_palette[16];
|
|
|
|
unsigned int index;
|
|
|
|
};
|
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
/**
|
|
|
|
* struct s3c_fb_vsync - vsync information
|
|
|
|
* @wait: a queue for processes waiting for vsync
|
|
|
|
* @count: vsync interrupt count
|
|
|
|
*/
|
|
|
|
struct s3c_fb_vsync {
|
|
|
|
wait_queue_head_t wait;
|
|
|
|
unsigned int count;
|
|
|
|
};
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
/**
|
|
|
|
* struct s3c_fb - overall hardware state of the hardware
|
2011-04-11 00:25:37 -07:00
|
|
|
* @slock: The spinlock protection for this data sturcture.
|
2009-03-31 15:25:39 -07:00
|
|
|
* @dev: The device that we bound to, for printing, etc.
|
|
|
|
* @regs_res: The resource we claimed for the IO registers.
|
|
|
|
* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
|
2011-08-21 20:16:04 -07:00
|
|
|
* @lcd_clk: The clk (sclk) feeding pixclk.
|
2009-03-31 15:25:39 -07:00
|
|
|
* @regs: The mapped hardware registers.
|
2010-08-10 18:02:33 -07:00
|
|
|
* @variant: Variant information for this hardware.
|
2009-03-31 15:25:39 -07:00
|
|
|
* @enabled: A bitmask of enabled hardware windows.
|
2011-12-27 07:16:10 -07:00
|
|
|
* @output_on: Flag if the physical output is enabled.
|
2009-03-31 15:25:39 -07:00
|
|
|
* @pdata: The platform configuration data passed with the device.
|
|
|
|
* @windows: The hardware windows that have been claimed.
|
2010-08-10 18:02:38 -07:00
|
|
|
* @irq_no: IRQ line number
|
|
|
|
* @irq_flags: irq flags
|
|
|
|
* @vsync_info: VSYNC-related information (count, queues...)
|
2009-03-31 15:25:39 -07:00
|
|
|
*/
|
|
|
|
struct s3c_fb {
|
2011-04-11 00:25:37 -07:00
|
|
|
spinlock_t slock;
|
2009-03-31 15:25:39 -07:00
|
|
|
struct device *dev;
|
|
|
|
struct resource *regs_res;
|
|
|
|
struct clk *bus_clk;
|
2011-08-21 20:16:04 -07:00
|
|
|
struct clk *lcd_clk;
|
2009-03-31 15:25:39 -07:00
|
|
|
void __iomem *regs;
|
2010-08-10 18:02:33 -07:00
|
|
|
struct s3c_fb_variant variant;
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
unsigned char enabled;
|
2011-12-27 07:16:10 -07:00
|
|
|
bool output_on;
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
struct s3c_fb_platdata *pdata;
|
|
|
|
struct s3c_fb_win *windows[S3C_FB_MAX_WIN];
|
2010-08-10 18:02:38 -07:00
|
|
|
|
|
|
|
int irq_no;
|
|
|
|
unsigned long irq_flags;
|
|
|
|
struct s3c_fb_vsync vsync_info;
|
2009-03-31 15:25:39 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2010-08-10 18:02:33 -07:00
|
|
|
* s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode.
|
|
|
|
* @win: The device window.
|
|
|
|
* @bpp: The bit depth.
|
2009-03-31 15:25:39 -07:00
|
|
|
*/
|
2010-08-10 18:02:33 -07:00
|
|
|
static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp)
|
2009-03-31 15:25:39 -07:00
|
|
|
{
|
2010-08-10 18:02:33 -07:00
|
|
|
return win->variant.valid_bpp & VALID_BPP(bpp);
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_check_var() - framebuffer layer request to verify a given mode.
|
|
|
|
* @var: The screen information to verify.
|
|
|
|
* @info: The framebuffer device.
|
|
|
|
*
|
|
|
|
* Framebuffer layer call to verify the given information and allow us to
|
|
|
|
* update various information depending on the hardware capabilities.
|
|
|
|
*/
|
|
|
|
static int s3c_fb_check_var(struct fb_var_screeninfo *var,
|
|
|
|
struct fb_info *info)
|
|
|
|
{
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "checking parameters\n");
|
|
|
|
|
2011-06-08 21:26:38 -07:00
|
|
|
var->xres_virtual = max(var->xres_virtual, var->xres);
|
|
|
|
var->yres_virtual = max(var->yres_virtual, var->yres);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {
|
2009-03-31 15:25:39 -07:00
|
|
|
dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",
|
|
|
|
win->index, var->bits_per_pixel);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* always ensure these are zero, for drop through cases below */
|
|
|
|
var->transp.offset = 0;
|
|
|
|
var->transp.length = 0;
|
|
|
|
|
|
|
|
switch (var->bits_per_pixel) {
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 4:
|
|
|
|
case 8:
|
2010-08-10 18:02:33 -07:00
|
|
|
if (sfb->variant.palette[win->index] != 0) {
|
2009-03-31 15:25:39 -07:00
|
|
|
/* non palletised, A:1,R:2,G:3,B:2 mode */
|
|
|
|
var->red.offset = 4;
|
|
|
|
var->green.offset = 2;
|
|
|
|
var->blue.offset = 0;
|
|
|
|
var->red.length = 5;
|
|
|
|
var->green.length = 3;
|
|
|
|
var->blue.length = 2;
|
|
|
|
var->transp.offset = 7;
|
|
|
|
var->transp.length = 1;
|
|
|
|
} else {
|
|
|
|
var->red.offset = 0;
|
|
|
|
var->red.length = var->bits_per_pixel;
|
|
|
|
var->green = var->red;
|
|
|
|
var->blue = var->red;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 19:
|
|
|
|
/* 666 with one bit alpha/transparency */
|
|
|
|
var->transp.offset = 18;
|
|
|
|
var->transp.length = 1;
|
|
|
|
case 18:
|
|
|
|
var->bits_per_pixel = 32;
|
|
|
|
|
|
|
|
/* 666 format */
|
|
|
|
var->red.offset = 12;
|
|
|
|
var->green.offset = 6;
|
|
|
|
var->blue.offset = 0;
|
|
|
|
var->red.length = 6;
|
|
|
|
var->green.length = 6;
|
|
|
|
var->blue.length = 6;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 16:
|
|
|
|
/* 16 bpp, 565 format */
|
|
|
|
var->red.offset = 11;
|
|
|
|
var->green.offset = 5;
|
|
|
|
var->blue.offset = 0;
|
|
|
|
var->red.length = 5;
|
|
|
|
var->green.length = 6;
|
|
|
|
var->blue.length = 5;
|
|
|
|
break;
|
|
|
|
|
2011-05-24 01:55:23 -07:00
|
|
|
case 32:
|
2009-03-31 15:25:39 -07:00
|
|
|
case 28:
|
|
|
|
case 25:
|
|
|
|
var->transp.length = var->bits_per_pixel - 24;
|
|
|
|
var->transp.offset = 24;
|
|
|
|
/* drop through */
|
|
|
|
case 24:
|
|
|
|
/* our 24bpp is unpacked, so 32bpp */
|
|
|
|
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;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
dev_err(sfb->dev, "invalid bpp\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "%s: verified parameters\n", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock.
|
|
|
|
* @sfb: The hardware state.
|
|
|
|
* @pixclock: The pixel clock wanted, in picoseconds.
|
|
|
|
*
|
|
|
|
* Given the specified pixel clock, work out the necessary divider to get
|
|
|
|
* close to the output frequency.
|
|
|
|
*/
|
2010-01-15 18:01:40 -07:00
|
|
|
static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk)
|
2009-03-31 15:25:39 -07:00
|
|
|
{
|
2011-08-21 20:16:04 -07:00
|
|
|
unsigned long clk;
|
2010-01-15 18:01:40 -07:00
|
|
|
unsigned long long tmp;
|
2009-03-31 15:25:39 -07:00
|
|
|
unsigned int result;
|
|
|
|
|
2011-08-21 20:16:04 -07:00
|
|
|
if (sfb->variant.has_clksel)
|
|
|
|
clk = clk_get_rate(sfb->bus_clk);
|
|
|
|
else
|
|
|
|
clk = clk_get_rate(sfb->lcd_clk);
|
|
|
|
|
2010-01-15 18:01:40 -07:00
|
|
|
tmp = (unsigned long long)clk;
|
|
|
|
tmp *= pixclk;
|
|
|
|
|
|
|
|
do_div(tmp, 1000000000UL);
|
|
|
|
result = (unsigned int)tmp / 1000;
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n",
|
|
|
|
pixclk, clk, result, clk / result);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_align_word() - align pixel count to word boundary
|
|
|
|
* @bpp: The number of bits per pixel
|
|
|
|
* @pix: The value to be aligned.
|
|
|
|
*
|
|
|
|
* Align the given pixel count so that it will start on an 32bit word
|
|
|
|
* boundary.
|
|
|
|
*/
|
|
|
|
static int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
|
|
|
|
{
|
|
|
|
int pix_per_word;
|
|
|
|
|
|
|
|
if (bpp > 16)
|
|
|
|
return pix;
|
|
|
|
|
|
|
|
pix_per_word = (8 * 32) / bpp;
|
|
|
|
return ALIGN(pix, pix_per_word);
|
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:40 -07:00
|
|
|
/**
|
|
|
|
* vidosd_set_size() - set OSD size for a window
|
|
|
|
*
|
|
|
|
* @win: the window to set OSD size for
|
|
|
|
* @size: OSD size register value
|
|
|
|
*/
|
|
|
|
static void vidosd_set_size(struct s3c_fb_win *win, u32 size)
|
|
|
|
{
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
|
|
|
|
/* OSD can be set up if osd_size_off != 0 for this window */
|
|
|
|
if (win->variant.osd_size_off)
|
|
|
|
writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)
|
|
|
|
+ win->variant.osd_size_off);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vidosd_set_alpha() - set alpha transparency for a window
|
|
|
|
*
|
|
|
|
* @win: the window to set OSD size for
|
|
|
|
* @alpha: alpha register value
|
|
|
|
*/
|
|
|
|
static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha)
|
|
|
|
{
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
|
|
|
|
if (win->variant.has_osd_alpha)
|
|
|
|
writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant));
|
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:40 -07:00
|
|
|
/**
|
|
|
|
* shadow_protect_win() - disable updating values from shadow registers at vsync
|
|
|
|
*
|
|
|
|
* @win: window to protect registers for
|
|
|
|
* @protect: 1 to protect (disable updates)
|
|
|
|
*/
|
|
|
|
static void shadow_protect_win(struct s3c_fb_win *win, bool protect)
|
|
|
|
{
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
u32 reg;
|
|
|
|
|
|
|
|
if (protect) {
|
|
|
|
if (sfb->variant.has_prtcon) {
|
|
|
|
writel(PRTCON_PROTECT, sfb->regs + PRTCON);
|
|
|
|
} else if (sfb->variant.has_shadowcon) {
|
|
|
|
reg = readl(sfb->regs + SHADOWCON);
|
|
|
|
writel(reg | SHADOWCON_WINx_PROTECT(win->index),
|
|
|
|
sfb->regs + SHADOWCON);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sfb->variant.has_prtcon) {
|
|
|
|
writel(0, sfb->regs + PRTCON);
|
|
|
|
} else if (sfb->variant.has_shadowcon) {
|
|
|
|
reg = readl(sfb->regs + SHADOWCON);
|
|
|
|
writel(reg & ~SHADOWCON_WINx_PROTECT(win->index),
|
|
|
|
sfb->regs + SHADOWCON);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-27 07:16:08 -07:00
|
|
|
/**
|
|
|
|
* s3c_fb_enable() - Set the state of the main LCD output
|
|
|
|
* @sfb: The main framebuffer state.
|
|
|
|
* @enable: The state to set.
|
|
|
|
*/
|
|
|
|
static void s3c_fb_enable(struct s3c_fb *sfb, int enable)
|
|
|
|
{
|
|
|
|
u32 vidcon0 = readl(sfb->regs + VIDCON0);
|
|
|
|
|
2011-12-27 07:16:10 -07:00
|
|
|
if (enable && !sfb->output_on)
|
|
|
|
pm_runtime_get_sync(sfb->dev);
|
|
|
|
|
|
|
|
if (enable) {
|
2011-12-27 07:16:08 -07:00
|
|
|
vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;
|
2011-12-27 07:16:10 -07:00
|
|
|
} else {
|
2011-12-27 07:16:08 -07:00
|
|
|
/* see the note in the framebuffer datasheet about
|
|
|
|
* why you cannot take both of these bits down at the
|
|
|
|
* same time. */
|
|
|
|
|
2011-12-27 07:16:10 -07:00
|
|
|
if (vidcon0 & VIDCON0_ENVID) {
|
|
|
|
vidcon0 |= VIDCON0_ENVID;
|
|
|
|
vidcon0 &= ~VIDCON0_ENVID_F;
|
|
|
|
}
|
2011-12-27 07:16:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
writel(vidcon0, sfb->regs + VIDCON0);
|
2011-12-27 07:16:10 -07:00
|
|
|
|
|
|
|
if (!enable && sfb->output_on)
|
|
|
|
pm_runtime_put_sync(sfb->dev);
|
|
|
|
|
|
|
|
sfb->output_on = enable;
|
2011-12-27 07:16:08 -07:00
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
/**
|
|
|
|
* s3c_fb_set_par() - framebuffer request to set new framebuffer state.
|
|
|
|
* @info: The framebuffer to change.
|
|
|
|
*
|
|
|
|
* Framebuffer layer request to set a new mode for the specified framebuffer
|
|
|
|
*/
|
|
|
|
static int s3c_fb_set_par(struct fb_info *info)
|
|
|
|
{
|
|
|
|
struct fb_var_screeninfo *var = &info->var;
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
void __iomem *regs = sfb->regs;
|
2010-08-10 18:02:34 -07:00
|
|
|
void __iomem *buf = regs;
|
2009-03-31 15:25:39 -07:00
|
|
|
int win_no = win->index;
|
2010-08-10 18:02:40 -07:00
|
|
|
u32 alpha = 0;
|
2009-03-31 15:25:39 -07:00
|
|
|
u32 data;
|
|
|
|
u32 pagewidth;
|
|
|
|
int clkdiv;
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "setting framebuffer parameters\n");
|
|
|
|
|
2010-08-10 18:02:41 -07:00
|
|
|
shadow_protect_win(win, 1);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
switch (var->bits_per_pixel) {
|
|
|
|
case 32:
|
|
|
|
case 24:
|
|
|
|
case 16:
|
|
|
|
case 12:
|
|
|
|
info->fix.visual = FB_VISUAL_TRUECOLOR;
|
|
|
|
break;
|
|
|
|
case 8:
|
2010-08-10 18:02:33 -07:00
|
|
|
if (win->variant.palette_sz >= 256)
|
2009-03-31 15:25:39 -07:00
|
|
|
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
|
|
|
|
else
|
|
|
|
info->fix.visual = FB_VISUAL_TRUECOLOR;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
info->fix.visual = FB_VISUAL_MONO01;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
|
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
|
|
|
|
info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
/* disable the window whilst we update it */
|
|
|
|
writel(0, regs + WINCON(win_no));
|
|
|
|
|
2010-08-10 18:02:31 -07:00
|
|
|
/* use platform specified window as the basis for the lcd timings */
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2010-08-10 18:02:31 -07:00
|
|
|
if (win_no == sfb->pdata->default_win) {
|
2010-01-15 18:01:40 -07:00
|
|
|
clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
data = sfb->pdata->vidcon0;
|
|
|
|
data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
|
|
|
|
|
|
|
|
if (clkdiv > 1)
|
|
|
|
data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
|
|
|
|
else
|
|
|
|
data &= ~VIDCON0_CLKDIR; /* 1:1 clock */
|
|
|
|
|
|
|
|
/* write the timing data to the panel */
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
if (sfb->variant.is_2443)
|
|
|
|
data |= (1 << 5);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
writel(data, regs + VIDCON0);
|
|
|
|
|
2011-12-27 07:16:08 -07:00
|
|
|
s3c_fb_enable(sfb, 1);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
data = VIDTCON0_VBPD(var->upper_margin - 1) |
|
|
|
|
VIDTCON0_VFPD(var->lower_margin - 1) |
|
|
|
|
VIDTCON0_VSPW(var->vsync_len - 1);
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(data, regs + sfb->variant.vidtcon);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
data = VIDTCON1_HBPD(var->left_margin - 1) |
|
|
|
|
VIDTCON1_HFPD(var->right_margin - 1) |
|
|
|
|
VIDTCON1_HSPW(var->hsync_len - 1);
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
/* VIDTCON1 */
|
|
|
|
writel(data, regs + sfb->variant.vidtcon + 4);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
data = VIDTCON2_LINEVAL(var->yres - 1) |
|
|
|
|
VIDTCON2_HOZVAL(var->xres - 1);
|
2011-04-01 00:17:27 -07:00
|
|
|
writel(data, regs + sfb->variant.vidtcon + 8);
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* write the buffer address */
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
/* start and end registers stride is 8 */
|
|
|
|
buf = regs + win_no * 8;
|
|
|
|
|
|
|
|
writel(info->fix.smem_start, buf + sfb->variant.buf_start);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
data = info->fix.smem_start + info->fix.line_length * var->yres;
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(data, buf + sfb->variant.buf_end);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
pagewidth = (var->xres * var->bits_per_pixel) >> 3;
|
|
|
|
data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
|
|
|
|
VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(data, regs + sfb->variant.buf_size + (win_no * 4));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
/* write 'OSD' registers to control position of framebuffer */
|
|
|
|
|
|
|
|
data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(data, regs + VIDOSD_A(win_no, sfb->variant));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
|
|
|
|
var->xres - 1)) |
|
|
|
|
VIDOSDxB_BOTRIGHT_Y(var->yres - 1);
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(data, regs + VIDOSD_B(win_no, sfb->variant));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
data = var->xres * var->yres;
|
Samsung SoC Framebuffer driver: add Alpha Channel support
Add support for the ARGB1888 and ARGB4888 hardware to the Samsung SoC
Framebuffer driver (s3c-fb.c).
ARGB1888 and ARGB4888 is decided by var->transp.length and this variable
is set by s3c_fb_check_var().
In s3c_fb_check_var(), if var->vits_per_pixel is 25 or 28, then
var->transp.length would be 1 or 3.
Therefore alpha mode(ARGB1888 or ARGB4888) could be decided through that
variable.
For using alpha mode, you need to set the following: This code should be
added to your machine code as platform data.
static struct s3c_fb_pd_win xxx_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 24,
};
static struct s3c_fb_pd_win xxx_fb_win1 = {
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 28,
};
static struct s3c_fb_platdata xxx_lcd_pdata __initdata = {
.win[0] = &ncp_fb_win0,
.win[1] = &ncp_fb_win1,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = xxx_fb_gpio_setup,
};
s3c_fb_set_platdata(&xxx_lcd_pdata);
The above code sets pixelformat for window0 layer to RGB888 and window1
layer to ARGB4888.
Signed-off-by: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kyungmin Park <kmpark@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-16 15:34:27 -07:00
|
|
|
|
2010-08-10 18:02:40 -07:00
|
|
|
alpha = VIDISD14C_ALPHA1_R(0xf) |
|
Samsung SoC Framebuffer driver: add Alpha Channel support
Add support for the ARGB1888 and ARGB4888 hardware to the Samsung SoC
Framebuffer driver (s3c-fb.c).
ARGB1888 and ARGB4888 is decided by var->transp.length and this variable
is set by s3c_fb_check_var().
In s3c_fb_check_var(), if var->vits_per_pixel is 25 or 28, then
var->transp.length would be 1 or 3.
Therefore alpha mode(ARGB1888 or ARGB4888) could be decided through that
variable.
For using alpha mode, you need to set the following: This code should be
added to your machine code as platform data.
static struct s3c_fb_pd_win xxx_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 24,
};
static struct s3c_fb_pd_win xxx_fb_win1 = {
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 28,
};
static struct s3c_fb_platdata xxx_lcd_pdata __initdata = {
.win[0] = &ncp_fb_win0,
.win[1] = &ncp_fb_win1,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = xxx_fb_gpio_setup,
};
s3c_fb_set_platdata(&xxx_lcd_pdata);
The above code sets pixelformat for window0 layer to RGB888 and window1
layer to ARGB4888.
Signed-off-by: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kyungmin Park <kmpark@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-16 15:34:27 -07:00
|
|
|
VIDISD14C_ALPHA1_G(0xf) |
|
|
|
|
VIDISD14C_ALPHA1_B(0xf);
|
|
|
|
|
2010-08-10 18:02:40 -07:00
|
|
|
vidosd_set_alpha(win, alpha);
|
|
|
|
vidosd_set_size(win, data);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2011-06-08 21:26:45 -07:00
|
|
|
/* Enable DMA channel for this window */
|
|
|
|
if (sfb->variant.has_shadowcon) {
|
|
|
|
data = readl(sfb->regs + SHADOWCON);
|
|
|
|
data |= SHADOWCON_CHx_ENABLE(win_no);
|
|
|
|
writel(data, sfb->regs + SHADOWCON);
|
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
data = WINCONx_ENWIN;
|
2011-12-02 03:07:17 -07:00
|
|
|
sfb->enabled |= (1 << win->index);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
/* note, since we have to round up the bits-per-pixel, we end up
|
|
|
|
* relying on the bitfield information for r/g/b/a to work out
|
|
|
|
* exactly which mode of operation is intended. */
|
|
|
|
|
|
|
|
switch (var->bits_per_pixel) {
|
|
|
|
case 1:
|
|
|
|
data |= WINCON0_BPPMODE_1BPP;
|
|
|
|
data |= WINCONx_BITSWP;
|
|
|
|
data |= WINCONx_BURSTLEN_4WORD;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
data |= WINCON0_BPPMODE_2BPP;
|
|
|
|
data |= WINCONx_BITSWP;
|
|
|
|
data |= WINCONx_BURSTLEN_8WORD;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
data |= WINCON0_BPPMODE_4BPP;
|
|
|
|
data |= WINCONx_BITSWP;
|
|
|
|
data |= WINCONx_BURSTLEN_8WORD;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (var->transp.length != 0)
|
|
|
|
data |= WINCON1_BPPMODE_8BPP_1232;
|
|
|
|
else
|
|
|
|
data |= WINCON0_BPPMODE_8BPP_PALETTE;
|
|
|
|
data |= WINCONx_BURSTLEN_8WORD;
|
|
|
|
data |= WINCONx_BYTSWP;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
if (var->transp.length != 0)
|
|
|
|
data |= WINCON1_BPPMODE_16BPP_A1555;
|
|
|
|
else
|
|
|
|
data |= WINCON0_BPPMODE_16BPP_565;
|
|
|
|
data |= WINCONx_HAWSWP;
|
|
|
|
data |= WINCONx_BURSTLEN_16WORD;
|
|
|
|
break;
|
|
|
|
case 24:
|
|
|
|
case 32:
|
|
|
|
if (var->red.length == 6) {
|
|
|
|
if (var->transp.length != 0)
|
|
|
|
data |= WINCON1_BPPMODE_19BPP_A1666;
|
|
|
|
else
|
|
|
|
data |= WINCON1_BPPMODE_18BPP_666;
|
Samsung SoC Framebuffer driver: add Alpha Channel support
Add support for the ARGB1888 and ARGB4888 hardware to the Samsung SoC
Framebuffer driver (s3c-fb.c).
ARGB1888 and ARGB4888 is decided by var->transp.length and this variable
is set by s3c_fb_check_var().
In s3c_fb_check_var(), if var->vits_per_pixel is 25 or 28, then
var->transp.length would be 1 or 3.
Therefore alpha mode(ARGB1888 or ARGB4888) could be decided through that
variable.
For using alpha mode, you need to set the following: This code should be
added to your machine code as platform data.
static struct s3c_fb_pd_win xxx_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 24,
};
static struct s3c_fb_pd_win xxx_fb_win1 = {
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 28,
};
static struct s3c_fb_platdata xxx_lcd_pdata __initdata = {
.win[0] = &ncp_fb_win0,
.win[1] = &ncp_fb_win1,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = xxx_fb_gpio_setup,
};
s3c_fb_set_platdata(&xxx_lcd_pdata);
The above code sets pixelformat for window0 layer to RGB888 and window1
layer to ARGB4888.
Signed-off-by: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kyungmin Park <kmpark@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-16 15:34:27 -07:00
|
|
|
} else if (var->transp.length == 1)
|
|
|
|
data |= WINCON1_BPPMODE_25BPP_A1888
|
|
|
|
| WINCON1_BLD_PIX;
|
2011-11-06 23:03:01 -07:00
|
|
|
else if ((var->transp.length == 4) ||
|
|
|
|
(var->transp.length == 8))
|
Samsung SoC Framebuffer driver: add Alpha Channel support
Add support for the ARGB1888 and ARGB4888 hardware to the Samsung SoC
Framebuffer driver (s3c-fb.c).
ARGB1888 and ARGB4888 is decided by var->transp.length and this variable
is set by s3c_fb_check_var().
In s3c_fb_check_var(), if var->vits_per_pixel is 25 or 28, then
var->transp.length would be 1 or 3.
Therefore alpha mode(ARGB1888 or ARGB4888) could be decided through that
variable.
For using alpha mode, you need to set the following: This code should be
added to your machine code as platform data.
static struct s3c_fb_pd_win xxx_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 24,
};
static struct s3c_fb_pd_win xxx_fb_win1 = {
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 28,
};
static struct s3c_fb_platdata xxx_lcd_pdata __initdata = {
.win[0] = &ncp_fb_win0,
.win[1] = &ncp_fb_win1,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = xxx_fb_gpio_setup,
};
s3c_fb_set_platdata(&xxx_lcd_pdata);
The above code sets pixelformat for window0 layer to RGB888 and window1
layer to ARGB4888.
Signed-off-by: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kyungmin Park <kmpark@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-16 15:34:27 -07:00
|
|
|
data |= WINCON1_BPPMODE_28BPP_A4888
|
|
|
|
| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
|
2009-03-31 15:25:39 -07:00
|
|
|
else
|
|
|
|
data |= WINCON0_BPPMODE_24BPP_888;
|
|
|
|
|
2010-08-10 18:02:32 -07:00
|
|
|
data |= WINCONx_WSWP;
|
2009-03-31 15:25:39 -07:00
|
|
|
data |= WINCONx_BURSTLEN_16WORD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
/* Enable the colour keying for the window below this one */
|
Samsung SoC Framebuffer driver: add Alpha Channel support
Add support for the ARGB1888 and ARGB4888 hardware to the Samsung SoC
Framebuffer driver (s3c-fb.c).
ARGB1888 and ARGB4888 is decided by var->transp.length and this variable
is set by s3c_fb_check_var().
In s3c_fb_check_var(), if var->vits_per_pixel is 25 or 28, then
var->transp.length would be 1 or 3.
Therefore alpha mode(ARGB1888 or ARGB4888) could be decided through that
variable.
For using alpha mode, you need to set the following: This code should be
added to your machine code as platform data.
static struct s3c_fb_pd_win xxx_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 24,
};
static struct s3c_fb_pd_win xxx_fb_win1 = {
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 28,
};
static struct s3c_fb_platdata xxx_lcd_pdata __initdata = {
.win[0] = &ncp_fb_win0,
.win[1] = &ncp_fb_win1,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = xxx_fb_gpio_setup,
};
s3c_fb_set_platdata(&xxx_lcd_pdata);
The above code sets pixelformat for window0 layer to RGB888 and window1
layer to ARGB4888.
Signed-off-by: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kyungmin Park <kmpark@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-16 15:34:27 -07:00
|
|
|
if (win_no > 0) {
|
|
|
|
u32 keycon0_data = 0, keycon1_data = 0;
|
2010-08-10 18:02:34 -07:00
|
|
|
void __iomem *keycon = regs + sfb->variant.keycon;
|
Samsung SoC Framebuffer driver: add Alpha Channel support
Add support for the ARGB1888 and ARGB4888 hardware to the Samsung SoC
Framebuffer driver (s3c-fb.c).
ARGB1888 and ARGB4888 is decided by var->transp.length and this variable
is set by s3c_fb_check_var().
In s3c_fb_check_var(), if var->vits_per_pixel is 25 or 28, then
var->transp.length would be 1 or 3.
Therefore alpha mode(ARGB1888 or ARGB4888) could be decided through that
variable.
For using alpha mode, you need to set the following: This code should be
added to your machine code as platform data.
static struct s3c_fb_pd_win xxx_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 24,
};
static struct s3c_fb_pd_win xxx_fb_win1 = {
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 28,
};
static struct s3c_fb_platdata xxx_lcd_pdata __initdata = {
.win[0] = &ncp_fb_win0,
.win[1] = &ncp_fb_win1,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = xxx_fb_gpio_setup,
};
s3c_fb_set_platdata(&xxx_lcd_pdata);
The above code sets pixelformat for window0 layer to RGB888 and window1
layer to ARGB4888.
Signed-off-by: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kyungmin Park <kmpark@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-16 15:34:27 -07:00
|
|
|
|
|
|
|
keycon0_data = ~(WxKEYCON0_KEYBL_EN |
|
|
|
|
WxKEYCON0_KEYEN_F |
|
|
|
|
WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
|
|
|
|
|
|
|
|
keycon1_data = WxKEYCON1_COLVAL(0xffffff);
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
keycon += (win_no - 1) * 8;
|
|
|
|
|
|
|
|
writel(keycon0_data, keycon + WKEYCON0);
|
|
|
|
writel(keycon1_data, keycon + WKEYCON1);
|
Samsung SoC Framebuffer driver: add Alpha Channel support
Add support for the ARGB1888 and ARGB4888 hardware to the Samsung SoC
Framebuffer driver (s3c-fb.c).
ARGB1888 and ARGB4888 is decided by var->transp.length and this variable
is set by s3c_fb_check_var().
In s3c_fb_check_var(), if var->vits_per_pixel is 25 or 28, then
var->transp.length would be 1 or 3.
Therefore alpha mode(ARGB1888 or ARGB4888) could be decided through that
variable.
For using alpha mode, you need to set the following: This code should be
added to your machine code as platform data.
static struct s3c_fb_pd_win xxx_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 24,
};
static struct s3c_fb_pd_win xxx_fb_win1 = {
.win_mode = {
.pixclock = (8+8+8+240)*(38+4+38+400),
.left_margin = 8,
.right_margin = 8,
.upper_margin = 38,
.lower_margin = 38,
.hsync_len = 8,
.vsync_len = 4,
.xres = 240,
.yres = 400,
},
.max_bpp = 32,
.default_bpp = 28,
};
static struct s3c_fb_platdata xxx_lcd_pdata __initdata = {
.win[0] = &ncp_fb_win0,
.win[1] = &ncp_fb_win1,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = xxx_fb_gpio_setup,
};
s3c_fb_set_platdata(&xxx_lcd_pdata);
The above code sets pixelformat for window0 layer to RGB888 and window1
layer to ARGB4888.
Signed-off-by: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kyungmin Park <kmpark@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-16 15:34:27 -07:00
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(data, regs + sfb->variant.wincon + (win_no * 4));
|
|
|
|
writel(0x0, regs + sfb->variant.winmap + (win_no * 4));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2010-08-10 18:02:41 -07:00
|
|
|
shadow_protect_win(win, 0);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_update_palette() - set or schedule a palette update.
|
|
|
|
* @sfb: The hardware information.
|
|
|
|
* @win: The window being updated.
|
|
|
|
* @reg: The palette index being changed.
|
|
|
|
* @value: The computed palette value.
|
|
|
|
*
|
|
|
|
* Change the value of a palette register, either by directly writing to
|
|
|
|
* the palette (this requires the palette RAM to be disconnected from the
|
|
|
|
* hardware whilst this is in progress) or schedule the update for later.
|
|
|
|
*
|
|
|
|
* At the moment, since we have no VSYNC interrupt support, we simply set
|
|
|
|
* the palette entry directly.
|
|
|
|
*/
|
|
|
|
static void s3c_fb_update_palette(struct s3c_fb *sfb,
|
|
|
|
struct s3c_fb_win *win,
|
|
|
|
unsigned int reg,
|
|
|
|
u32 value)
|
|
|
|
{
|
|
|
|
void __iomem *palreg;
|
|
|
|
u32 palcon;
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
palreg = sfb->regs + sfb->variant.palette[win->index];
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",
|
|
|
|
__func__, win->index, reg, palreg, value);
|
|
|
|
|
|
|
|
win->palette_buffer[reg] = value;
|
|
|
|
|
|
|
|
palcon = readl(sfb->regs + WPALCON);
|
|
|
|
writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
if (win->variant.palette_16bpp)
|
|
|
|
writew(value, palreg + (reg * 2));
|
2009-03-31 15:25:39 -07:00
|
|
|
else
|
2010-08-10 18:02:33 -07:00
|
|
|
writel(value, palreg + (reg * 4));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
writel(palcon, sfb->regs + WPALCON);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int chan_to_field(unsigned int chan,
|
|
|
|
struct fb_bitfield *bf)
|
|
|
|
{
|
|
|
|
chan &= 0xffff;
|
|
|
|
chan >>= 16 - bf->length;
|
|
|
|
return chan << bf->offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_setcolreg() - framebuffer layer request to change palette.
|
|
|
|
* @regno: The palette index to change.
|
|
|
|
* @red: The red field for the palette data.
|
|
|
|
* @green: The green field for the palette data.
|
|
|
|
* @blue: The blue field for the palette data.
|
|
|
|
* @trans: The transparency (alpha) field for the palette data.
|
|
|
|
* @info: The framebuffer being changed.
|
|
|
|
*/
|
|
|
|
static int s3c_fb_setcolreg(unsigned regno,
|
|
|
|
unsigned red, unsigned green, unsigned blue,
|
|
|
|
unsigned transp, struct fb_info *info)
|
|
|
|
{
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
unsigned int val;
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n",
|
|
|
|
__func__, win->index, regno, red, green, blue);
|
|
|
|
|
|
|
|
switch (info->fix.visual) {
|
|
|
|
case FB_VISUAL_TRUECOLOR:
|
|
|
|
/* true-colour, use pseudo-palette */
|
|
|
|
|
|
|
|
if (regno < 16) {
|
|
|
|
u32 *pal = info->pseudo_palette;
|
|
|
|
|
|
|
|
val = chan_to_field(red, &info->var.red);
|
|
|
|
val |= chan_to_field(green, &info->var.green);
|
|
|
|
val |= chan_to_field(blue, &info->var.blue);
|
|
|
|
|
|
|
|
pal[regno] = val;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FB_VISUAL_PSEUDOCOLOR:
|
2010-08-10 18:02:33 -07:00
|
|
|
if (regno < win->variant.palette_sz) {
|
2009-03-31 15:25:39 -07:00
|
|
|
val = chan_to_field(red, &win->palette.r);
|
|
|
|
val |= chan_to_field(green, &win->palette.g);
|
|
|
|
val |= chan_to_field(blue, &win->palette.b);
|
|
|
|
|
|
|
|
s3c_fb_update_palette(sfb, win, regno, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 1; /* unknown type */
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_blank() - blank or unblank the given window
|
|
|
|
* @blank_mode: The blank state from FB_BLANK_*
|
|
|
|
* @info: The framebuffer to blank.
|
|
|
|
*
|
|
|
|
* Framebuffer layer request to change the power state.
|
|
|
|
*/
|
|
|
|
static int s3c_fb_blank(int blank_mode, struct fb_info *info)
|
|
|
|
{
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
unsigned int index = win->index;
|
|
|
|
u32 wincon;
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
switch (blank_mode) {
|
|
|
|
case FB_BLANK_POWERDOWN:
|
|
|
|
wincon &= ~WINCONx_ENWIN;
|
|
|
|
sfb->enabled &= ~(1 << index);
|
|
|
|
/* fall through to FB_BLANK_NORMAL */
|
|
|
|
|
|
|
|
case FB_BLANK_NORMAL:
|
|
|
|
/* disable the DMA and display 0x0 (black) */
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 1);
|
2009-03-31 15:25:39 -07:00
|
|
|
writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
|
2010-08-10 18:02:34 -07:00
|
|
|
sfb->regs + sfb->variant.winmap + (index * 4));
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 0);
|
2009-03-31 15:25:39 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FB_BLANK_UNBLANK:
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 1);
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4));
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 0);
|
2009-03-31 15:25:39 -07:00
|
|
|
wincon |= WINCONx_ENWIN;
|
|
|
|
sfb->enabled |= (1 << index);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FB_BLANK_VSYNC_SUSPEND:
|
|
|
|
case FB_BLANK_HSYNC_SUSPEND:
|
|
|
|
default:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 1);
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4));
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 0);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
/* Check the enabled state to see if we need to be running the
|
|
|
|
* main LCD interface, as if there are no active windows then
|
|
|
|
* it is highly likely that we also do not need to output
|
|
|
|
* anything.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* We could do something like the following code, but the current
|
|
|
|
* system of using framebuffer events means that we cannot make
|
|
|
|
* the distinction between just window 0 being inactive and all
|
|
|
|
* the windows being down.
|
|
|
|
*
|
|
|
|
* s3c_fb_enable(sfb, sfb->enabled ? 1 : 0);
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* we're stuck with this until we can do something about overriding
|
|
|
|
* the power control using the blanking event for a single fb.
|
|
|
|
*/
|
2011-12-08 02:08:00 -07:00
|
|
|
if (index == sfb->pdata->default_win) {
|
|
|
|
shadow_protect_win(win, 1);
|
2009-03-31 15:25:39 -07:00
|
|
|
s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 0);
|
|
|
|
}
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
/**
|
|
|
|
* s3c_fb_pan_display() - Pan the display.
|
|
|
|
*
|
|
|
|
* Note that the offsets can be written to the device at any time, as their
|
|
|
|
* values are latched at each vsync automatically. This also means that only
|
|
|
|
* the last call to this function will have any effect on next vsync, but
|
|
|
|
* there is no need to sleep waiting for it to prevent tearing.
|
|
|
|
*
|
|
|
|
* @var: The screen information to verify.
|
|
|
|
* @info: The framebuffer device.
|
|
|
|
*/
|
|
|
|
static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
|
|
|
|
struct fb_info *info)
|
|
|
|
{
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
void __iomem *buf = sfb->regs + win->index * 8;
|
|
|
|
unsigned int start_boff, end_boff;
|
|
|
|
|
|
|
|
/* Offset in bytes to the start of the displayed area */
|
|
|
|
start_boff = var->yoffset * info->fix.line_length;
|
|
|
|
/* X offset depends on the current bpp */
|
|
|
|
if (info->var.bits_per_pixel >= 8) {
|
|
|
|
start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);
|
|
|
|
} else {
|
|
|
|
switch (info->var.bits_per_pixel) {
|
|
|
|
case 4:
|
|
|
|
start_boff += var->xoffset >> 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
start_boff += var->xoffset >> 2;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
start_boff += var->xoffset >> 3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_err(sfb->dev, "invalid bpp\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Offset in bytes to the end of the displayed area */
|
2011-05-25 02:34:52 -07:00
|
|
|
end_boff = start_boff + info->var.yres * info->fix.line_length;
|
2010-08-10 18:02:38 -07:00
|
|
|
|
|
|
|
/* Temporarily turn off per-vsync update from shadow registers until
|
|
|
|
* both start and end addresses are updated to prevent corruption */
|
2010-08-10 18:02:40 -07:00
|
|
|
shadow_protect_win(win, 1);
|
2010-08-10 18:02:38 -07:00
|
|
|
|
|
|
|
writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start);
|
|
|
|
writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end);
|
|
|
|
|
2010-08-10 18:02:40 -07:00
|
|
|
shadow_protect_win(win, 0);
|
2010-08-10 18:02:38 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
/**
|
|
|
|
* s3c_fb_enable_irq() - enable framebuffer interrupts
|
|
|
|
* @sfb: main hardware state
|
|
|
|
*/
|
|
|
|
static void s3c_fb_enable_irq(struct s3c_fb *sfb)
|
|
|
|
{
|
|
|
|
void __iomem *regs = sfb->regs;
|
|
|
|
u32 irq_ctrl_reg;
|
|
|
|
|
|
|
|
if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
|
|
|
|
/* IRQ disabled, enable it */
|
|
|
|
irq_ctrl_reg = readl(regs + VIDINTCON0);
|
|
|
|
|
|
|
|
irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;
|
|
|
|
irq_ctrl_reg |= VIDINTCON0_INT_FRAME;
|
|
|
|
|
|
|
|
irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;
|
|
|
|
irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;
|
|
|
|
irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;
|
|
|
|
irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;
|
|
|
|
|
|
|
|
writel(irq_ctrl_reg, regs + VIDINTCON0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_disable_irq() - disable framebuffer interrupts
|
|
|
|
* @sfb: main hardware state
|
|
|
|
*/
|
|
|
|
static void s3c_fb_disable_irq(struct s3c_fb *sfb)
|
|
|
|
{
|
|
|
|
void __iomem *regs = sfb->regs;
|
|
|
|
u32 irq_ctrl_reg;
|
|
|
|
|
|
|
|
if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
|
|
|
|
/* IRQ enabled, disable it */
|
|
|
|
irq_ctrl_reg = readl(regs + VIDINTCON0);
|
|
|
|
|
|
|
|
irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;
|
|
|
|
irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;
|
|
|
|
|
|
|
|
writel(irq_ctrl_reg, regs + VIDINTCON0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t s3c_fb_irq(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct s3c_fb *sfb = dev_id;
|
|
|
|
void __iomem *regs = sfb->regs;
|
|
|
|
u32 irq_sts_reg;
|
|
|
|
|
2011-04-11 00:25:37 -07:00
|
|
|
spin_lock(&sfb->slock);
|
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
irq_sts_reg = readl(regs + VIDINTCON1);
|
|
|
|
|
|
|
|
if (irq_sts_reg & VIDINTCON1_INT_FRAME) {
|
|
|
|
|
|
|
|
/* VSYNC interrupt, accept it */
|
|
|
|
writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1);
|
|
|
|
|
|
|
|
sfb->vsync_info.count++;
|
|
|
|
wake_up_interruptible(&sfb->vsync_info.wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We only support waiting for VSYNC for now, so it's safe
|
|
|
|
* to always disable irqs here.
|
|
|
|
*/
|
|
|
|
s3c_fb_disable_irq(sfb);
|
|
|
|
|
2011-04-11 00:25:37 -07:00
|
|
|
spin_unlock(&sfb->slock);
|
2010-08-10 18:02:38 -07:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout
|
|
|
|
* @sfb: main hardware state
|
|
|
|
* @crtc: head index.
|
|
|
|
*/
|
|
|
|
static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
|
|
|
|
{
|
|
|
|
unsigned long count;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (crtc != 0)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
count = sfb->vsync_info.count;
|
|
|
|
s3c_fb_enable_irq(sfb);
|
|
|
|
ret = wait_event_interruptible_timeout(sfb->vsync_info.wait,
|
|
|
|
count != sfb->vsync_info.count,
|
|
|
|
msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));
|
|
|
|
if (ret == 0)
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
int ret;
|
|
|
|
u32 crtc;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case FBIO_WAITFORVSYNC:
|
|
|
|
if (get_user(crtc, (u32 __user *)arg)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = s3c_fb_wait_for_vsync(sfb, crtc);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -ENOTTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-12-27 07:16:07 -07:00
|
|
|
static int s3c_fb_open(struct fb_info *info, int user)
|
|
|
|
{
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(sfb->dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int s3c_fb_release(struct fb_info *info, int user)
|
|
|
|
{
|
|
|
|
struct s3c_fb_win *win = info->par;
|
|
|
|
struct s3c_fb *sfb = win->parent;
|
|
|
|
|
|
|
|
pm_runtime_put_sync(sfb->dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
static struct fb_ops s3c_fb_ops = {
|
|
|
|
.owner = THIS_MODULE,
|
2011-12-27 07:16:07 -07:00
|
|
|
.fb_open = s3c_fb_open,
|
|
|
|
.fb_release = s3c_fb_release,
|
2009-03-31 15:25:39 -07:00
|
|
|
.fb_check_var = s3c_fb_check_var,
|
|
|
|
.fb_set_par = s3c_fb_set_par,
|
|
|
|
.fb_blank = s3c_fb_blank,
|
|
|
|
.fb_setcolreg = s3c_fb_setcolreg,
|
|
|
|
.fb_fillrect = cfb_fillrect,
|
|
|
|
.fb_copyarea = cfb_copyarea,
|
|
|
|
.fb_imageblit = cfb_imageblit,
|
2010-08-10 18:02:38 -07:00
|
|
|
.fb_pan_display = s3c_fb_pan_display,
|
2010-08-10 18:02:38 -07:00
|
|
|
.fb_ioctl = s3c_fb_ioctl,
|
2009-03-31 15:25:39 -07:00
|
|
|
};
|
|
|
|
|
2010-08-10 18:02:44 -07:00
|
|
|
/**
|
|
|
|
* s3c_fb_missing_pixclock() - calculates pixel clock
|
|
|
|
* @mode: The video mode to change.
|
|
|
|
*
|
|
|
|
* Calculate the pixel clock when none has been given through platform data.
|
|
|
|
*/
|
|
|
|
static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode)
|
|
|
|
{
|
|
|
|
u64 pixclk = 1000000000000ULL;
|
|
|
|
u32 div;
|
|
|
|
|
|
|
|
div = mode->left_margin + mode->hsync_len + mode->right_margin +
|
|
|
|
mode->xres;
|
|
|
|
div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +
|
|
|
|
mode->yres;
|
|
|
|
div *= mode->refresh ? : 60;
|
|
|
|
|
|
|
|
do_div(pixclk, div);
|
|
|
|
|
|
|
|
mode->pixclock = pixclk;
|
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
/**
|
|
|
|
* s3c_fb_alloc_memory() - allocate display memory for framebuffer window
|
|
|
|
* @sfb: The base resources for the hardware.
|
|
|
|
* @win: The window to initialise memory for.
|
|
|
|
*
|
|
|
|
* Allocate memory for the given framebuffer.
|
|
|
|
*/
|
|
|
|
static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb,
|
|
|
|
struct s3c_fb_win *win)
|
|
|
|
{
|
|
|
|
struct s3c_fb_pd_win *windata = win->windata;
|
|
|
|
unsigned int real_size, virt_size, size;
|
|
|
|
struct fb_info *fbi = win->fbinfo;
|
|
|
|
dma_addr_t map_dma;
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "allocating memory for display\n");
|
|
|
|
|
|
|
|
real_size = windata->win_mode.xres * windata->win_mode.yres;
|
|
|
|
virt_size = windata->virtual_x * windata->virtual_y;
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",
|
|
|
|
real_size, windata->win_mode.xres, windata->win_mode.yres,
|
|
|
|
virt_size, windata->virtual_x, windata->virtual_y);
|
|
|
|
|
|
|
|
size = (real_size > virt_size) ? real_size : virt_size;
|
|
|
|
size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;
|
|
|
|
size /= 8;
|
|
|
|
|
|
|
|
fbi->fix.smem_len = size;
|
|
|
|
size = PAGE_ALIGN(size);
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "want %u bytes for window\n", size);
|
|
|
|
|
|
|
|
fbi->screen_base = dma_alloc_writecombine(sfb->dev, size,
|
|
|
|
&map_dma, GFP_KERNEL);
|
|
|
|
if (!fbi->screen_base)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "mapped %x to %p\n",
|
|
|
|
(unsigned int)map_dma, fbi->screen_base);
|
|
|
|
|
|
|
|
memset(fbi->screen_base, 0x0, size);
|
|
|
|
fbi->fix.smem_start = map_dma;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_free_memory() - free the display memory for the given window
|
|
|
|
* @sfb: The base resources for the hardware.
|
|
|
|
* @win: The window to free the display memory for.
|
|
|
|
*
|
|
|
|
* Free the display memory allocated by s3c_fb_alloc_memory().
|
|
|
|
*/
|
|
|
|
static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
|
|
|
|
{
|
|
|
|
struct fb_info *fbi = win->fbinfo;
|
|
|
|
|
2010-08-10 18:02:35 -07:00
|
|
|
if (fbi->screen_base)
|
|
|
|
dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
|
2009-03-31 15:25:39 -07:00
|
|
|
fbi->screen_base, fbi->fix.smem_start);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_release_win() - release resources for a framebuffer window.
|
|
|
|
* @win: The window to cleanup the resources for.
|
|
|
|
*
|
|
|
|
* Release the resources that where claimed for the hardware window,
|
|
|
|
* such as the framebuffer instance and any memory claimed for it.
|
|
|
|
*/
|
|
|
|
static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
|
|
|
|
{
|
2010-08-10 18:02:43 -07:00
|
|
|
u32 data;
|
|
|
|
|
2009-06-16 15:34:33 -07:00
|
|
|
if (win->fbinfo) {
|
2010-08-10 18:02:43 -07:00
|
|
|
if (sfb->variant.has_shadowcon) {
|
|
|
|
data = readl(sfb->regs + SHADOWCON);
|
|
|
|
data &= ~SHADOWCON_CHx_ENABLE(win->index);
|
|
|
|
data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index);
|
|
|
|
writel(data, sfb->regs + SHADOWCON);
|
|
|
|
}
|
2009-06-16 15:34:33 -07:00
|
|
|
unregister_framebuffer(win->fbinfo);
|
2010-08-10 18:02:35 -07:00
|
|
|
if (win->fbinfo->cmap.len)
|
|
|
|
fb_dealloc_cmap(&win->fbinfo->cmap);
|
2009-06-16 15:34:33 -07:00
|
|
|
s3c_fb_free_memory(sfb, win);
|
|
|
|
framebuffer_release(win->fbinfo);
|
|
|
|
}
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_probe_win() - register an hardware window
|
|
|
|
* @sfb: The base resources for the hardware
|
2010-08-10 18:02:33 -07:00
|
|
|
* @variant: The variant information for this window.
|
2009-03-31 15:25:39 -07:00
|
|
|
* @res: Pointer to where to place the resultant window.
|
|
|
|
*
|
|
|
|
* Allocate and do the basic initialisation for one of the hardware's graphics
|
|
|
|
* windows.
|
|
|
|
*/
|
|
|
|
static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
|
2010-08-10 18:02:33 -07:00
|
|
|
struct s3c_fb_win_variant *variant,
|
2009-03-31 15:25:39 -07:00
|
|
|
struct s3c_fb_win **res)
|
|
|
|
{
|
|
|
|
struct fb_var_screeninfo *var;
|
|
|
|
struct fb_videomode *initmode;
|
|
|
|
struct s3c_fb_pd_win *windata;
|
|
|
|
struct s3c_fb_win *win;
|
|
|
|
struct fb_info *fbinfo;
|
|
|
|
int palette_size;
|
|
|
|
int ret;
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
init_waitqueue_head(&sfb->vsync_info.wait);
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
palette_size = variant->palette_sz * 4;
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
|
|
|
|
palette_size * sizeof(u32), sfb->dev);
|
|
|
|
if (!fbinfo) {
|
|
|
|
dev_err(sfb->dev, "failed to allocate framebuffer\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
windata = sfb->pdata->win[win_no];
|
|
|
|
initmode = &windata->win_mode;
|
|
|
|
|
|
|
|
WARN_ON(windata->max_bpp == 0);
|
|
|
|
WARN_ON(windata->win_mode.xres == 0);
|
|
|
|
WARN_ON(windata->win_mode.yres == 0);
|
|
|
|
|
|
|
|
win = fbinfo->par;
|
2010-08-10 18:02:35 -07:00
|
|
|
*res = win;
|
2009-03-31 15:25:39 -07:00
|
|
|
var = &fbinfo->var;
|
2010-08-10 18:02:33 -07:00
|
|
|
win->variant = *variant;
|
2009-03-31 15:25:39 -07:00
|
|
|
win->fbinfo = fbinfo;
|
|
|
|
win->parent = sfb;
|
|
|
|
win->windata = windata;
|
|
|
|
win->index = win_no;
|
|
|
|
win->palette_buffer = (u32 *)(win + 1);
|
|
|
|
|
|
|
|
ret = s3c_fb_alloc_memory(sfb, win);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(sfb->dev, "failed to allocate display memory\n");
|
2009-06-16 15:34:33 -07:00
|
|
|
return ret;
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* setup the r/b/g positions for the window's palette */
|
2010-08-10 18:02:34 -07:00
|
|
|
if (win->variant.palette_16bpp) {
|
|
|
|
/* Set RGB 5:6:5 as default */
|
|
|
|
win->palette.r.offset = 11;
|
|
|
|
win->palette.r.length = 5;
|
|
|
|
win->palette.g.offset = 5;
|
|
|
|
win->palette.g.length = 6;
|
|
|
|
win->palette.b.offset = 0;
|
|
|
|
win->palette.b.length = 5;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Set 8bpp or 8bpp and 1bit alpha */
|
|
|
|
win->palette.r.offset = 16;
|
|
|
|
win->palette.r.length = 8;
|
|
|
|
win->palette.g.offset = 8;
|
|
|
|
win->palette.g.length = 8;
|
|
|
|
win->palette.b.offset = 0;
|
|
|
|
win->palette.b.length = 8;
|
|
|
|
}
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
/* setup the initial video mode from the window */
|
|
|
|
fb_videomode_to_var(&fbinfo->var, initmode);
|
|
|
|
|
|
|
|
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
|
|
|
|
fbinfo->fix.accel = FB_ACCEL_NONE;
|
|
|
|
fbinfo->var.activate = FB_ACTIVATE_NOW;
|
|
|
|
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
|
|
|
|
fbinfo->var.bits_per_pixel = windata->default_bpp;
|
|
|
|
fbinfo->fbops = &s3c_fb_ops;
|
|
|
|
fbinfo->flags = FBINFO_FLAG_DEFAULT;
|
|
|
|
fbinfo->pseudo_palette = &win->pseudo_palette;
|
|
|
|
|
|
|
|
/* prepare to actually start the framebuffer */
|
|
|
|
|
|
|
|
ret = s3c_fb_check_var(&fbinfo->var, fbinfo);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(sfb->dev, "check_var failed on initial video params\n");
|
2009-06-16 15:34:33 -07:00
|
|
|
return ret;
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* create initial colour map */
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
|
2009-03-31 15:25:39 -07:00
|
|
|
if (ret == 0)
|
|
|
|
fb_set_cmap(&fbinfo->cmap, fbinfo);
|
|
|
|
else
|
|
|
|
dev_err(sfb->dev, "failed to allocate fb cmap\n");
|
|
|
|
|
|
|
|
s3c_fb_set_par(fbinfo);
|
|
|
|
|
|
|
|
dev_dbg(sfb->dev, "about to register framebuffer\n");
|
|
|
|
|
|
|
|
/* run the check_var and set_par on our configuration. */
|
|
|
|
|
|
|
|
ret = register_framebuffer(fbinfo);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(sfb->dev, "failed to register framebuffer\n");
|
2009-06-16 15:34:33 -07:00
|
|
|
return ret;
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_clear_win() - clear hardware window registers.
|
|
|
|
* @sfb: The base resources for the hardware.
|
|
|
|
* @win: The window to process.
|
|
|
|
*
|
|
|
|
* Reset the specific window registers to a known state.
|
|
|
|
*/
|
|
|
|
static void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
|
|
|
|
{
|
|
|
|
void __iomem *regs = sfb->regs;
|
2010-08-10 18:02:41 -07:00
|
|
|
u32 reg;
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
writel(0, regs + sfb->variant.wincon + (win * 4));
|
|
|
|
writel(0, regs + VIDOSD_A(win, sfb->variant));
|
|
|
|
writel(0, regs + VIDOSD_B(win, sfb->variant));
|
|
|
|
writel(0, regs + VIDOSD_C(win, sfb->variant));
|
2010-08-10 18:02:41 -07:00
|
|
|
reg = readl(regs + SHADOWCON);
|
|
|
|
writel(reg & ~SHADOWCON_WINx_PROTECT(win), regs + SHADOWCON);
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __devinit s3c_fb_probe(struct platform_device *pdev)
|
|
|
|
{
|
2011-04-01 00:17:27 -07:00
|
|
|
const struct platform_device_id *platid;
|
2010-08-10 18:02:33 -07:00
|
|
|
struct s3c_fb_driverdata *fbdrv;
|
2009-03-31 15:25:39 -07:00
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
struct s3c_fb_platdata *pd;
|
|
|
|
struct s3c_fb *sfb;
|
|
|
|
struct resource *res;
|
|
|
|
int win;
|
|
|
|
int ret = 0;
|
|
|
|
|
2011-04-01 00:17:27 -07:00
|
|
|
platid = platform_get_device_id(pdev);
|
|
|
|
fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;
|
2010-08-10 18:02:33 -07:00
|
|
|
|
|
|
|
if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
|
|
|
|
dev_err(dev, "too many windows, cannot attach\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
pd = pdev->dev.platform_data;
|
|
|
|
if (!pd) {
|
|
|
|
dev_err(dev, "no platform data specified\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);
|
|
|
|
if (!sfb) {
|
|
|
|
dev_err(dev, "no memory for framebuffers\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
sfb->dev = dev;
|
|
|
|
sfb->pdata = pd;
|
2010-08-10 18:02:33 -07:00
|
|
|
sfb->variant = fbdrv->variant;
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2011-04-11 00:25:37 -07:00
|
|
|
spin_lock_init(&sfb->slock);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
sfb->bus_clk = clk_get(dev, "lcd");
|
|
|
|
if (IS_ERR(sfb->bus_clk)) {
|
|
|
|
dev_err(dev, "failed to get bus clock\n");
|
2011-02-11 01:51:10 -07:00
|
|
|
ret = PTR_ERR(sfb->bus_clk);
|
2009-03-31 15:25:39 -07:00
|
|
|
goto err_sfb;
|
|
|
|
}
|
|
|
|
|
|
|
|
clk_enable(sfb->bus_clk);
|
|
|
|
|
2011-08-21 20:16:04 -07:00
|
|
|
if (!sfb->variant.has_clksel) {
|
|
|
|
sfb->lcd_clk = clk_get(dev, "sclk_fimd");
|
|
|
|
if (IS_ERR(sfb->lcd_clk)) {
|
|
|
|
dev_err(dev, "failed to get lcd clock\n");
|
|
|
|
ret = PTR_ERR(sfb->lcd_clk);
|
|
|
|
goto err_bus_clk;
|
|
|
|
}
|
|
|
|
|
|
|
|
clk_enable(sfb->lcd_clk);
|
|
|
|
}
|
|
|
|
|
2010-12-17 00:45:46 -07:00
|
|
|
pm_runtime_enable(sfb->dev);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
if (!res) {
|
|
|
|
dev_err(dev, "failed to find registers\n");
|
|
|
|
ret = -ENOENT;
|
2011-08-21 20:16:04 -07:00
|
|
|
goto err_lcd_clk;
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
sfb->regs_res = request_mem_region(res->start, resource_size(res),
|
|
|
|
dev_name(dev));
|
|
|
|
if (!sfb->regs_res) {
|
|
|
|
dev_err(dev, "failed to claim register region\n");
|
|
|
|
ret = -ENOENT;
|
2011-08-21 20:16:04 -07:00
|
|
|
goto err_lcd_clk;
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
sfb->regs = ioremap(res->start, resource_size(res));
|
|
|
|
if (!sfb->regs) {
|
|
|
|
dev_err(dev, "failed to map registers\n");
|
|
|
|
ret = -ENXIO;
|
|
|
|
goto err_req_region;
|
|
|
|
}
|
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
|
|
if (!res) {
|
|
|
|
dev_err(dev, "failed to acquire irq resource\n");
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto err_ioremap;
|
|
|
|
}
|
|
|
|
sfb->irq_no = res->start;
|
|
|
|
ret = request_irq(sfb->irq_no, s3c_fb_irq,
|
|
|
|
0, "s3c_fb", sfb);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "irq request failed\n");
|
|
|
|
goto err_ioremap;
|
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
|
|
|
|
|
2010-12-17 00:45:46 -07:00
|
|
|
platform_set_drvdata(pdev, sfb);
|
|
|
|
pm_runtime_get_sync(sfb->dev);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
/* setup gpio and output polarity controls */
|
|
|
|
|
|
|
|
pd->setup_gpio();
|
|
|
|
|
|
|
|
writel(pd->vidcon1, sfb->regs + VIDCON1);
|
|
|
|
|
|
|
|
/* zero all windows before we do anything */
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
for (win = 0; win < fbdrv->variant.nr_windows; win++)
|
2009-03-31 15:25:39 -07:00
|
|
|
s3c_fb_clear_win(sfb, win);
|
|
|
|
|
2010-08-10 18:02:32 -07:00
|
|
|
/* initialise colour key controls */
|
2010-08-10 18:02:33 -07:00
|
|
|
for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
|
2010-08-10 18:02:34 -07:00
|
|
|
void __iomem *regs = sfb->regs + sfb->variant.keycon;
|
|
|
|
|
|
|
|
regs += (win * 8);
|
|
|
|
writel(0xffffff, regs + WKEYCON0);
|
|
|
|
writel(0xffffff, regs + WKEYCON1);
|
2010-08-10 18:02:32 -07:00
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
/* we have the register setup, start allocating framebuffers */
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
for (win = 0; win < fbdrv->variant.nr_windows; win++) {
|
2009-03-31 15:25:39 -07:00
|
|
|
if (!pd->win[win])
|
|
|
|
continue;
|
|
|
|
|
2010-08-10 18:02:44 -07:00
|
|
|
if (!pd->win[win]->win_mode.pixclock)
|
|
|
|
s3c_fb_missing_pixclock(&pd->win[win]->win_mode);
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
|
|
|
|
&sfb->windows[win]);
|
2009-03-31 15:25:39 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "failed to create window %d\n", win);
|
|
|
|
for (; win >= 0; win--)
|
|
|
|
s3c_fb_release_win(sfb, sfb->windows[win]);
|
2011-12-27 07:16:09 -07:00
|
|
|
goto err_pm_runtime;
|
2009-03-31 15:25:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
platform_set_drvdata(pdev, sfb);
|
2011-12-27 07:16:07 -07:00
|
|
|
pm_runtime_put_sync(sfb->dev);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2011-12-27 07:16:09 -07:00
|
|
|
err_pm_runtime:
|
|
|
|
pm_runtime_put_sync(sfb->dev);
|
2010-08-10 18:02:38 -07:00
|
|
|
free_irq(sfb->irq_no, sfb);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
err_ioremap:
|
|
|
|
iounmap(sfb->regs);
|
|
|
|
|
|
|
|
err_req_region:
|
2011-04-22 13:11:21 -07:00
|
|
|
release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2011-08-21 20:16:04 -07:00
|
|
|
err_lcd_clk:
|
2011-12-27 07:16:09 -07:00
|
|
|
pm_runtime_disable(sfb->dev);
|
|
|
|
|
2011-08-21 20:16:04 -07:00
|
|
|
if (!sfb->variant.has_clksel) {
|
|
|
|
clk_disable(sfb->lcd_clk);
|
|
|
|
clk_put(sfb->lcd_clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
err_bus_clk:
|
2009-03-31 15:25:39 -07:00
|
|
|
clk_disable(sfb->bus_clk);
|
|
|
|
clk_put(sfb->bus_clk);
|
|
|
|
|
|
|
|
err_sfb:
|
|
|
|
kfree(sfb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* s3c_fb_remove() - Cleanup on module finalisation
|
|
|
|
* @pdev: The platform device we are bound to.
|
|
|
|
*
|
|
|
|
* Shutdown and then release all the resources that the driver allocated
|
|
|
|
* on initialisation.
|
|
|
|
*/
|
|
|
|
static int __devexit s3c_fb_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct s3c_fb *sfb = platform_get_drvdata(pdev);
|
|
|
|
int win;
|
|
|
|
|
2011-12-27 07:16:07 -07:00
|
|
|
pm_runtime_get_sync(sfb->dev);
|
|
|
|
|
2009-07-29 15:02:10 -07:00
|
|
|
for (win = 0; win < S3C_FB_MAX_WIN; win++)
|
2009-05-28 14:34:35 -07:00
|
|
|
if (sfb->windows[win])
|
|
|
|
s3c_fb_release_win(sfb, sfb->windows[win]);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2010-08-10 18:02:38 -07:00
|
|
|
free_irq(sfb->irq_no, sfb);
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
iounmap(sfb->regs);
|
|
|
|
|
2011-08-21 20:16:04 -07:00
|
|
|
if (!sfb->variant.has_clksel) {
|
|
|
|
clk_disable(sfb->lcd_clk);
|
|
|
|
clk_put(sfb->lcd_clk);
|
|
|
|
}
|
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
clk_disable(sfb->bus_clk);
|
|
|
|
clk_put(sfb->bus_clk);
|
|
|
|
|
2011-04-22 13:11:21 -07:00
|
|
|
release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res));
|
2009-03-31 15:25:39 -07:00
|
|
|
|
2010-12-17 00:45:46 -07:00
|
|
|
pm_runtime_put_sync(sfb->dev);
|
|
|
|
pm_runtime_disable(sfb->dev);
|
|
|
|
|
2011-06-08 21:26:31 -07:00
|
|
|
kfree(sfb);
|
2009-03-31 15:25:39 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-27 07:16:10 -07:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2010-12-17 00:45:46 -07:00
|
|
|
static int s3c_fb_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct s3c_fb *sfb = platform_get_drvdata(pdev);
|
|
|
|
struct s3c_fb_win *win;
|
|
|
|
int win_no;
|
|
|
|
|
|
|
|
for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) {
|
|
|
|
win = sfb->windows[win_no];
|
|
|
|
if (!win)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* use the blank function to push into power-down */
|
|
|
|
s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo);
|
|
|
|
}
|
|
|
|
|
2011-08-21 20:16:04 -07:00
|
|
|
if (!sfb->variant.has_clksel)
|
|
|
|
clk_disable(sfb->lcd_clk);
|
|
|
|
|
2010-12-17 00:45:46 -07:00
|
|
|
clk_disable(sfb->bus_clk);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int s3c_fb_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct s3c_fb *sfb = platform_get_drvdata(pdev);
|
|
|
|
struct s3c_fb_platdata *pd = sfb->pdata;
|
|
|
|
struct s3c_fb_win *win;
|
|
|
|
int win_no;
|
|
|
|
|
|
|
|
clk_enable(sfb->bus_clk);
|
|
|
|
|
2011-08-21 20:16:04 -07:00
|
|
|
if (!sfb->variant.has_clksel)
|
|
|
|
clk_enable(sfb->lcd_clk);
|
|
|
|
|
2011-05-24 01:55:31 -07:00
|
|
|
/* setup gpio and output polarity controls */
|
|
|
|
pd->setup_gpio();
|
2010-12-17 00:45:46 -07:00
|
|
|
writel(pd->vidcon1, sfb->regs + VIDCON1);
|
|
|
|
|
|
|
|
/* zero all windows before we do anything */
|
|
|
|
for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
|
|
|
|
s3c_fb_clear_win(sfb, win_no);
|
|
|
|
|
|
|
|
for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
|
|
|
|
void __iomem *regs = sfb->regs + sfb->variant.keycon;
|
2011-12-08 02:08:00 -07:00
|
|
|
win = sfb->windows[win_no];
|
|
|
|
if (!win)
|
|
|
|
continue;
|
2010-12-17 00:45:46 -07:00
|
|
|
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 1);
|
2010-12-17 00:45:46 -07:00
|
|
|
regs += (win_no * 8);
|
|
|
|
writel(0xffffff, regs + WKEYCON0);
|
|
|
|
writel(0xffffff, regs + WKEYCON1);
|
2011-12-08 02:08:00 -07:00
|
|
|
shadow_protect_win(win, 0);
|
2010-12-17 00:45:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* restore framebuffers */
|
|
|
|
for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
|
|
|
|
win = sfb->windows[win_no];
|
|
|
|
if (!win)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dev_dbg(&pdev->dev, "resuming window %d\n", win_no);
|
|
|
|
s3c_fb_set_par(win->fbinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-03-31 15:25:39 -07:00
|
|
|
#endif
|
|
|
|
|
2011-12-27 07:16:10 -07:00
|
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
|
|
static int s3c_fb_runtime_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct s3c_fb *sfb = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
if (!sfb->variant.has_clksel)
|
|
|
|
clk_disable(sfb->lcd_clk);
|
|
|
|
|
|
|
|
clk_disable(sfb->bus_clk);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int s3c_fb_runtime_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct s3c_fb *sfb = platform_get_drvdata(pdev);
|
|
|
|
struct s3c_fb_platdata *pd = sfb->pdata;
|
|
|
|
|
|
|
|
clk_enable(sfb->bus_clk);
|
|
|
|
|
|
|
|
if (!sfb->variant.has_clksel)
|
|
|
|
clk_enable(sfb->lcd_clk);
|
|
|
|
|
|
|
|
/* setup gpio and output polarity controls */
|
|
|
|
pd->setup_gpio();
|
|
|
|
writel(pd->vidcon1, sfb->regs + VIDCON1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2010-08-10 18:02:33 -07:00
|
|
|
|
|
|
|
#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
|
|
|
|
#define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
|
|
|
|
|
s3c-fb: fix section mismatch
This patch fixes the following section mismatch errors:
WARNING: vmlinux.o(.data+0x20b40): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b58): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b70): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 18:02:42 -07:00
|
|
|
static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = {
|
2010-08-10 18:02:33 -07:00
|
|
|
[0] = {
|
|
|
|
.has_osd_c = 1,
|
2010-08-10 18:02:40 -07:00
|
|
|
.osd_size_off = 0x8,
|
2010-08-10 18:02:33 -07:00
|
|
|
.palette_sz = 256,
|
2011-04-22 00:09:40 -07:00
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(24)),
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
|
|
|
[1] = {
|
|
|
|
.has_osd_c = 1,
|
|
|
|
.has_osd_d = 1,
|
2011-04-22 00:09:31 -07:00
|
|
|
.osd_size_off = 0xc,
|
2010-08-10 18:02:40 -07:00
|
|
|
.has_osd_alpha = 1,
|
2010-08-10 18:02:33 -07:00
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
2011-04-22 00:09:40 -07:00
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(28)),
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
|
|
|
[2] = {
|
|
|
|
.has_osd_c = 1,
|
|
|
|
.has_osd_d = 1,
|
2011-04-22 00:09:31 -07:00
|
|
|
.osd_size_off = 0xc,
|
2010-08-10 18:02:40 -07:00
|
|
|
.has_osd_alpha = 1,
|
2010-08-10 18:02:33 -07:00
|
|
|
.palette_sz = 16,
|
|
|
|
.palette_16bpp = 1,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
2011-04-22 00:09:40 -07:00
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(28)),
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
|
|
|
[3] = {
|
|
|
|
.has_osd_c = 1,
|
2010-08-10 18:02:40 -07:00
|
|
|
.has_osd_alpha = 1,
|
2010-08-10 18:02:33 -07:00
|
|
|
.palette_sz = 16,
|
|
|
|
.palette_16bpp = 1,
|
|
|
|
.valid_bpp = (VALID_BPP124 | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
2011-04-22 00:09:40 -07:00
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(28)),
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
|
|
|
[4] = {
|
|
|
|
.has_osd_c = 1,
|
2010-08-10 18:02:40 -07:00
|
|
|
.has_osd_alpha = 1,
|
2010-08-10 18:02:33 -07:00
|
|
|
.palette_sz = 4,
|
|
|
|
.palette_16bpp = 1,
|
|
|
|
.valid_bpp = (VALID_BPP(1) | VALID_BPP(2) |
|
|
|
|
VALID_BPP(16) | VALID_BPP(18) |
|
2011-04-22 00:09:40 -07:00
|
|
|
VALID_BPP(19) | VALID_BPP(24) |
|
|
|
|
VALID_BPP(25) | VALID_BPP(28)),
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-04-22 00:09:48 -07:00
|
|
|
static struct s3c_fb_win_variant s3c_fb_data_s5p_wins[] = {
|
|
|
|
[0] = {
|
|
|
|
.has_osd_c = 1,
|
|
|
|
.osd_size_off = 0x8,
|
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
|
|
|
|
VALID_BPP(15) | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(32)),
|
|
|
|
},
|
|
|
|
[1] = {
|
|
|
|
.has_osd_c = 1,
|
|
|
|
.has_osd_d = 1,
|
|
|
|
.osd_size_off = 0xc,
|
|
|
|
.has_osd_alpha = 1,
|
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
|
|
|
|
VALID_BPP(15) | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(32)),
|
|
|
|
},
|
|
|
|
[2] = {
|
|
|
|
.has_osd_c = 1,
|
|
|
|
.has_osd_d = 1,
|
|
|
|
.osd_size_off = 0xc,
|
|
|
|
.has_osd_alpha = 1,
|
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
|
|
|
|
VALID_BPP(15) | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(32)),
|
|
|
|
},
|
|
|
|
[3] = {
|
|
|
|
.has_osd_c = 1,
|
|
|
|
.has_osd_alpha = 1,
|
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
|
|
|
|
VALID_BPP(15) | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(32)),
|
|
|
|
},
|
|
|
|
[4] = {
|
|
|
|
.has_osd_c = 1,
|
|
|
|
.has_osd_alpha = 1,
|
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
|
|
|
|
VALID_BPP(15) | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(32)),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
s3c-fb: fix section mismatch
This patch fixes the following section mismatch errors:
WARNING: vmlinux.o(.data+0x20b40): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b58): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b70): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 18:02:42 -07:00
|
|
|
static struct s3c_fb_driverdata s3c_fb_data_64xx = {
|
2010-08-10 18:02:33 -07:00
|
|
|
.variant = {
|
|
|
|
.nr_windows = 5,
|
2010-08-10 18:02:34 -07:00
|
|
|
.vidtcon = VIDTCON0,
|
|
|
|
.wincon = WINCON(0),
|
|
|
|
.winmap = WINxMAP(0),
|
|
|
|
.keycon = WKEYCON,
|
|
|
|
.osd = VIDOSD_BASE,
|
|
|
|
.osd_stride = 16,
|
|
|
|
.buf_start = VIDW_BUF_START(0),
|
|
|
|
.buf_size = VIDW_BUF_SIZE(0),
|
|
|
|
.buf_end = VIDW_BUF_END(0),
|
2010-08-10 18:02:33 -07:00
|
|
|
|
|
|
|
.palette = {
|
|
|
|
[0] = 0x400,
|
|
|
|
[1] = 0x800,
|
|
|
|
[2] = 0x300,
|
|
|
|
[3] = 0x320,
|
|
|
|
[4] = 0x340,
|
|
|
|
},
|
2010-08-10 18:02:38 -07:00
|
|
|
|
|
|
|
.has_prtcon = 1,
|
2011-08-21 20:16:04 -07:00
|
|
|
.has_clksel = 1,
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
|
|
|
.win[0] = &s3c_fb_data_64xx_wins[0],
|
|
|
|
.win[1] = &s3c_fb_data_64xx_wins[1],
|
|
|
|
.win[2] = &s3c_fb_data_64xx_wins[2],
|
|
|
|
.win[3] = &s3c_fb_data_64xx_wins[3],
|
|
|
|
.win[4] = &s3c_fb_data_64xx_wins[4],
|
|
|
|
};
|
|
|
|
|
s3c-fb: fix section mismatch
This patch fixes the following section mismatch errors:
WARNING: vmlinux.o(.data+0x20b40): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b58): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b70): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 18:02:42 -07:00
|
|
|
static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = {
|
2010-08-10 18:02:36 -07:00
|
|
|
.variant = {
|
|
|
|
.nr_windows = 5,
|
|
|
|
.vidtcon = VIDTCON0,
|
|
|
|
.wincon = WINCON(0),
|
|
|
|
.winmap = WINxMAP(0),
|
|
|
|
.keycon = WKEYCON,
|
|
|
|
.osd = VIDOSD_BASE,
|
|
|
|
.osd_stride = 16,
|
|
|
|
.buf_start = VIDW_BUF_START(0),
|
|
|
|
.buf_size = VIDW_BUF_SIZE(0),
|
|
|
|
.buf_end = VIDW_BUF_END(0),
|
|
|
|
|
|
|
|
.palette = {
|
|
|
|
[0] = 0x2400,
|
|
|
|
[1] = 0x2800,
|
|
|
|
[2] = 0x2c00,
|
|
|
|
[3] = 0x3000,
|
|
|
|
[4] = 0x3400,
|
|
|
|
},
|
2010-08-10 18:02:38 -07:00
|
|
|
|
|
|
|
.has_prtcon = 1,
|
2011-08-21 20:16:04 -07:00
|
|
|
.has_clksel = 1,
|
2010-08-10 18:02:36 -07:00
|
|
|
},
|
2011-04-22 00:09:48 -07:00
|
|
|
.win[0] = &s3c_fb_data_s5p_wins[0],
|
|
|
|
.win[1] = &s3c_fb_data_s5p_wins[1],
|
|
|
|
.win[2] = &s3c_fb_data_s5p_wins[2],
|
|
|
|
.win[3] = &s3c_fb_data_s5p_wins[3],
|
|
|
|
.win[4] = &s3c_fb_data_s5p_wins[4],
|
2010-08-10 18:02:36 -07:00
|
|
|
};
|
|
|
|
|
s3c-fb: fix section mismatch
This patch fixes the following section mismatch errors:
WARNING: vmlinux.o(.data+0x20b40): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b58): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b70): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 18:02:42 -07:00
|
|
|
static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
|
2011-08-21 20:16:04 -07:00
|
|
|
.variant = {
|
|
|
|
.nr_windows = 5,
|
|
|
|
.vidtcon = VIDTCON0,
|
|
|
|
.wincon = WINCON(0),
|
|
|
|
.winmap = WINxMAP(0),
|
|
|
|
.keycon = WKEYCON,
|
|
|
|
.osd = VIDOSD_BASE,
|
|
|
|
.osd_stride = 16,
|
|
|
|
.buf_start = VIDW_BUF_START(0),
|
|
|
|
.buf_size = VIDW_BUF_SIZE(0),
|
|
|
|
.buf_end = VIDW_BUF_END(0),
|
|
|
|
|
|
|
|
.palette = {
|
|
|
|
[0] = 0x2400,
|
|
|
|
[1] = 0x2800,
|
|
|
|
[2] = 0x2c00,
|
|
|
|
[3] = 0x3000,
|
|
|
|
[4] = 0x3400,
|
|
|
|
},
|
|
|
|
|
|
|
|
.has_shadowcon = 1,
|
|
|
|
.has_clksel = 1,
|
|
|
|
},
|
|
|
|
.win[0] = &s3c_fb_data_s5p_wins[0],
|
|
|
|
.win[1] = &s3c_fb_data_s5p_wins[1],
|
|
|
|
.win[2] = &s3c_fb_data_s5p_wins[2],
|
|
|
|
.win[3] = &s3c_fb_data_s5p_wins[3],
|
|
|
|
.win[4] = &s3c_fb_data_s5p_wins[4],
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {
|
2010-08-10 18:02:33 -07:00
|
|
|
.variant = {
|
|
|
|
.nr_windows = 5,
|
2010-08-10 18:02:34 -07:00
|
|
|
.vidtcon = VIDTCON0,
|
|
|
|
.wincon = WINCON(0),
|
|
|
|
.winmap = WINxMAP(0),
|
|
|
|
.keycon = WKEYCON,
|
|
|
|
.osd = VIDOSD_BASE,
|
|
|
|
.osd_stride = 16,
|
|
|
|
.buf_start = VIDW_BUF_START(0),
|
|
|
|
.buf_size = VIDW_BUF_SIZE(0),
|
|
|
|
.buf_end = VIDW_BUF_END(0),
|
2010-08-10 18:02:33 -07:00
|
|
|
|
|
|
|
.palette = {
|
|
|
|
[0] = 0x2400,
|
|
|
|
[1] = 0x2800,
|
|
|
|
[2] = 0x2c00,
|
|
|
|
[3] = 0x3000,
|
|
|
|
[4] = 0x3400,
|
|
|
|
},
|
2010-08-10 18:02:40 -07:00
|
|
|
|
|
|
|
.has_shadowcon = 1,
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
2011-04-22 00:09:48 -07:00
|
|
|
.win[0] = &s3c_fb_data_s5p_wins[0],
|
|
|
|
.win[1] = &s3c_fb_data_s5p_wins[1],
|
|
|
|
.win[2] = &s3c_fb_data_s5p_wins[2],
|
|
|
|
.win[3] = &s3c_fb_data_s5p_wins[3],
|
|
|
|
.win[4] = &s3c_fb_data_s5p_wins[4],
|
2010-08-10 18:02:33 -07:00
|
|
|
};
|
|
|
|
|
2010-08-10 18:02:34 -07:00
|
|
|
/* S3C2443/S3C2416 style hardware */
|
s3c-fb: fix section mismatch
This patch fixes the following section mismatch errors:
WARNING: vmlinux.o(.data+0x20b40): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b58): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
WARNING: vmlinux.o(.data+0x20b70): Section mismatch in reference from the variable s3c_fb_driver_ids to the (unknown reference) .devinit.data:(unknown)
The variable s3c_fb_driver_ids references
the (unknown reference) __devinitdata (unknown)
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console,
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 18:02:42 -07:00
|
|
|
static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
|
2010-08-10 18:02:34 -07:00
|
|
|
.variant = {
|
|
|
|
.nr_windows = 2,
|
|
|
|
.is_2443 = 1,
|
|
|
|
|
|
|
|
.vidtcon = 0x08,
|
|
|
|
.wincon = 0x14,
|
|
|
|
.winmap = 0xd0,
|
|
|
|
.keycon = 0xb0,
|
|
|
|
.osd = 0x28,
|
|
|
|
.osd_stride = 12,
|
|
|
|
.buf_start = 0x64,
|
|
|
|
.buf_size = 0x94,
|
|
|
|
.buf_end = 0x7c,
|
|
|
|
|
|
|
|
.palette = {
|
|
|
|
[0] = 0x400,
|
|
|
|
[1] = 0x800,
|
|
|
|
},
|
2011-08-21 20:16:04 -07:00
|
|
|
.has_clksel = 1,
|
2010-08-10 18:02:34 -07:00
|
|
|
},
|
|
|
|
.win[0] = &(struct s3c_fb_win_variant) {
|
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
|
|
|
|
},
|
|
|
|
.win[1] = &(struct s3c_fb_win_variant) {
|
|
|
|
.has_osd_c = 1,
|
2010-08-10 18:02:40 -07:00
|
|
|
.has_osd_alpha = 1,
|
2010-08-10 18:02:34 -07:00
|
|
|
.palette_sz = 256,
|
|
|
|
.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
|
|
|
|
VALID_BPP(18) | VALID_BPP(19) |
|
|
|
|
VALID_BPP(24) | VALID_BPP(25) |
|
|
|
|
VALID_BPP(28)),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-09-09 11:00:51 -07:00
|
|
|
static struct s3c_fb_driverdata s3c_fb_data_s5p64x0 = {
|
|
|
|
.variant = {
|
|
|
|
.nr_windows = 3,
|
|
|
|
.vidtcon = VIDTCON0,
|
|
|
|
.wincon = WINCON(0),
|
|
|
|
.winmap = WINxMAP(0),
|
|
|
|
.keycon = WKEYCON,
|
|
|
|
.osd = VIDOSD_BASE,
|
|
|
|
.osd_stride = 16,
|
|
|
|
.buf_start = VIDW_BUF_START(0),
|
|
|
|
.buf_size = VIDW_BUF_SIZE(0),
|
|
|
|
.buf_end = VIDW_BUF_END(0),
|
|
|
|
|
|
|
|
.palette = {
|
|
|
|
[0] = 0x2400,
|
|
|
|
[1] = 0x2800,
|
|
|
|
[2] = 0x2c00,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
.win[0] = &s3c_fb_data_s5p_wins[0],
|
|
|
|
.win[1] = &s3c_fb_data_s5p_wins[1],
|
|
|
|
.win[2] = &s3c_fb_data_s5p_wins[2],
|
|
|
|
};
|
|
|
|
|
2010-08-10 18:02:33 -07:00
|
|
|
static struct platform_device_id s3c_fb_driver_ids[] = {
|
|
|
|
{
|
|
|
|
.name = "s3c-fb",
|
|
|
|
.driver_data = (unsigned long)&s3c_fb_data_64xx,
|
|
|
|
}, {
|
2010-08-10 18:02:36 -07:00
|
|
|
.name = "s5pc100-fb",
|
|
|
|
.driver_data = (unsigned long)&s3c_fb_data_s5pc100,
|
|
|
|
}, {
|
|
|
|
.name = "s5pv210-fb",
|
|
|
|
.driver_data = (unsigned long)&s3c_fb_data_s5pv210,
|
2011-08-21 20:16:04 -07:00
|
|
|
}, {
|
|
|
|
.name = "exynos4-fb",
|
|
|
|
.driver_data = (unsigned long)&s3c_fb_data_exynos4,
|
2010-08-10 18:02:34 -07:00
|
|
|
}, {
|
|
|
|
.name = "s3c2443-fb",
|
|
|
|
.driver_data = (unsigned long)&s3c_fb_data_s3c2443,
|
2011-09-09 11:00:51 -07:00
|
|
|
}, {
|
|
|
|
.name = "s5p64x0-fb",
|
|
|
|
.driver_data = (unsigned long)&s3c_fb_data_s5p64x0,
|
2010-08-10 18:02:33 -07:00
|
|
|
},
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
|
|
|
|
|
2011-12-27 07:16:10 -07:00
|
|
|
static const struct dev_pm_ops s3cfb_pm_ops = {
|
|
|
|
SET_SYSTEM_SLEEP_PM_OPS(s3c_fb_suspend, s3c_fb_resume)
|
|
|
|
SET_RUNTIME_PM_OPS(s3c_fb_runtime_suspend, s3c_fb_runtime_resume,
|
|
|
|
NULL)
|
|
|
|
};
|
2010-12-17 00:45:46 -07:00
|
|
|
|
2009-03-31 15:25:39 -07:00
|
|
|
static struct platform_driver s3c_fb_driver = {
|
|
|
|
.probe = s3c_fb_probe,
|
2009-09-22 16:47:55 -07:00
|
|
|
.remove = __devexit_p(s3c_fb_remove),
|
2010-08-10 18:02:33 -07:00
|
|
|
.id_table = s3c_fb_driver_ids,
|
2009-03-31 15:25:39 -07:00
|
|
|
.driver = {
|
|
|
|
.name = "s3c-fb",
|
|
|
|
.owner = THIS_MODULE,
|
2011-12-27 07:16:07 -07:00
|
|
|
.pm = &s3cfb_pm_ops,
|
2009-03-31 15:25:39 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-11-25 19:25:54 -07:00
|
|
|
module_platform_driver(s3c_fb_driver);
|
2009-03-31 15:25:39 -07:00
|
|
|
|
|
|
|
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
|
|
|
MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS("platform:s3c-fb");
|