162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* linux/drivers/video/s3c-fb.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 2008 Openmoko Inc.
562306a36Sopenharmony_ci * Copyright 2008-2010 Simtec Electronics
662306a36Sopenharmony_ci *      Ben Dooks <ben@simtec.co.uk>
762306a36Sopenharmony_ci *      http://armlinux.simtec.co.uk/
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Samsung SoC Framebuffer driver
1062306a36Sopenharmony_ci*/
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/clk.h>
1962306a36Sopenharmony_ci#include <linux/fb.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/uaccess.h>
2262306a36Sopenharmony_ci#include <linux/interrupt.h>
2362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2462306a36Sopenharmony_ci#include <linux/platform_data/video_s3c.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <video/samsung_fimd.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* This driver will export a number of framebuffer interfaces depending
2962306a36Sopenharmony_ci * on the configuration passed in via the platform data. Each fb instance
3062306a36Sopenharmony_ci * maps to a hardware window. Currently there is no support for runtime
3162306a36Sopenharmony_ci * setting of the alpha-blending functions that each window has, so only
3262306a36Sopenharmony_ci * window 0 is actually useful.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Window 0 is treated specially, it is used for the basis of the LCD
3562306a36Sopenharmony_ci * output timings and as the control for the output power-down state.
3662306a36Sopenharmony_ci*/
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* note, the previous use of <mach/regs-fb.h> to get platform specific data
3962306a36Sopenharmony_ci * has been replaced by using the platform device name to pick the correct
4062306a36Sopenharmony_ci * configuration data for the system.
4162306a36Sopenharmony_ci*/
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE
4462306a36Sopenharmony_ci#undef writel
4562306a36Sopenharmony_ci#define writel(v, r) do { \
4662306a36Sopenharmony_ci	pr_debug("%s: %08x => %p\n", __func__, (unsigned int)v, r); \
4762306a36Sopenharmony_ci	__raw_writel(v, r); \
4862306a36Sopenharmony_ci} while (0)
4962306a36Sopenharmony_ci#endif /* FB_S3C_DEBUG_REGWRITE */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* irq_flags bits */
5262306a36Sopenharmony_ci#define S3C_FB_VSYNC_IRQ_EN	0
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define VSYNC_TIMEOUT_MSEC 50
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct s3c_fb;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define VALID_BPP(x) (1 << ((x) - 1))
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride))
6162306a36Sopenharmony_ci#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00)
6262306a36Sopenharmony_ci#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04)
6362306a36Sopenharmony_ci#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08)
6462306a36Sopenharmony_ci#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/**
6762306a36Sopenharmony_ci * struct s3c_fb_variant - fb variant information
6862306a36Sopenharmony_ci * @is_2443: Set if S3C2443/S3C2416 style hardware.
6962306a36Sopenharmony_ci * @nr_windows: The number of windows.
7062306a36Sopenharmony_ci * @vidtcon: The base for the VIDTCONx registers
7162306a36Sopenharmony_ci * @wincon: The base for the WINxCON registers.
7262306a36Sopenharmony_ci * @winmap: The base for the WINxMAP registers.
7362306a36Sopenharmony_ci * @keycon: The abse for the WxKEYCON registers.
7462306a36Sopenharmony_ci * @buf_start: Offset of buffer start registers.
7562306a36Sopenharmony_ci * @buf_size: Offset of buffer size registers.
7662306a36Sopenharmony_ci * @buf_end: Offset of buffer end registers.
7762306a36Sopenharmony_ci * @osd: The base for the OSD registers.
7862306a36Sopenharmony_ci * @osd_stride: stride of osd
7962306a36Sopenharmony_ci * @palette: Address of palette memory, or 0 if none.
8062306a36Sopenharmony_ci * @has_prtcon: Set if has PRTCON register.
8162306a36Sopenharmony_ci * @has_shadowcon: Set if has SHADOWCON register.
8262306a36Sopenharmony_ci * @has_blendcon: Set if has BLENDCON register.
8362306a36Sopenharmony_ci * @has_clksel: Set if VIDCON0 register has CLKSEL bit.
8462306a36Sopenharmony_ci * @has_fixvclk: Set if VIDCON1 register has FIXVCLK bits.
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistruct s3c_fb_variant {
8762306a36Sopenharmony_ci	unsigned int	is_2443:1;
8862306a36Sopenharmony_ci	unsigned short	nr_windows;
8962306a36Sopenharmony_ci	unsigned int	vidtcon;
9062306a36Sopenharmony_ci	unsigned short	wincon;
9162306a36Sopenharmony_ci	unsigned short	winmap;
9262306a36Sopenharmony_ci	unsigned short	keycon;
9362306a36Sopenharmony_ci	unsigned short	buf_start;
9462306a36Sopenharmony_ci	unsigned short	buf_end;
9562306a36Sopenharmony_ci	unsigned short	buf_size;
9662306a36Sopenharmony_ci	unsigned short	osd;
9762306a36Sopenharmony_ci	unsigned short	osd_stride;
9862306a36Sopenharmony_ci	unsigned short	palette[S3C_FB_MAX_WIN];
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	unsigned int	has_prtcon:1;
10162306a36Sopenharmony_ci	unsigned int	has_shadowcon:1;
10262306a36Sopenharmony_ci	unsigned int	has_blendcon:1;
10362306a36Sopenharmony_ci	unsigned int	has_clksel:1;
10462306a36Sopenharmony_ci	unsigned int	has_fixvclk:1;
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/**
10862306a36Sopenharmony_ci * struct s3c_fb_win_variant
10962306a36Sopenharmony_ci * @has_osd_c: Set if has OSD C register.
11062306a36Sopenharmony_ci * @has_osd_d: Set if has OSD D register.
11162306a36Sopenharmony_ci * @has_osd_alpha: Set if can change alpha transparency for a window.
11262306a36Sopenharmony_ci * @palette_sz: Size of palette in entries.
11362306a36Sopenharmony_ci * @palette_16bpp: Set if palette is 16bits wide.
11462306a36Sopenharmony_ci * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate
11562306a36Sopenharmony_ci *                register is located at the given offset from OSD_BASE.
11662306a36Sopenharmony_ci * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel.
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * valid_bpp bit x is set if (x+1)BPP is supported.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistruct s3c_fb_win_variant {
12162306a36Sopenharmony_ci	unsigned int	has_osd_c:1;
12262306a36Sopenharmony_ci	unsigned int	has_osd_d:1;
12362306a36Sopenharmony_ci	unsigned int	has_osd_alpha:1;
12462306a36Sopenharmony_ci	unsigned int	palette_16bpp:1;
12562306a36Sopenharmony_ci	unsigned short	osd_size_off;
12662306a36Sopenharmony_ci	unsigned short	palette_sz;
12762306a36Sopenharmony_ci	u32		valid_bpp;
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/**
13162306a36Sopenharmony_ci * struct s3c_fb_driverdata - per-device type driver data for init time.
13262306a36Sopenharmony_ci * @variant: The variant information for this driver.
13362306a36Sopenharmony_ci * @win: The window information for each window.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_cistruct s3c_fb_driverdata {
13662306a36Sopenharmony_ci	struct s3c_fb_variant	variant;
13762306a36Sopenharmony_ci	struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN];
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/**
14162306a36Sopenharmony_ci * struct s3c_fb_palette - palette information
14262306a36Sopenharmony_ci * @r: Red bitfield.
14362306a36Sopenharmony_ci * @g: Green bitfield.
14462306a36Sopenharmony_ci * @b: Blue bitfield.
14562306a36Sopenharmony_ci * @a: Alpha bitfield.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistruct s3c_fb_palette {
14862306a36Sopenharmony_ci	struct fb_bitfield	r;
14962306a36Sopenharmony_ci	struct fb_bitfield	g;
15062306a36Sopenharmony_ci	struct fb_bitfield	b;
15162306a36Sopenharmony_ci	struct fb_bitfield	a;
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/**
15562306a36Sopenharmony_ci * struct s3c_fb_win - per window private data for each framebuffer.
15662306a36Sopenharmony_ci * @windata: The platform data supplied for the window configuration.
15762306a36Sopenharmony_ci * @parent: The hardware that this window is part of.
15862306a36Sopenharmony_ci * @fbinfo: Pointer pack to the framebuffer info for this window.
15962306a36Sopenharmony_ci * @variant: The variant information for this window.
16062306a36Sopenharmony_ci * @palette_buffer: Buffer/cache to hold palette entries.
16162306a36Sopenharmony_ci * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
16262306a36Sopenharmony_ci * @index: The window number of this window.
16362306a36Sopenharmony_ci * @palette: The bitfields for changing r/g/b into a hardware palette entry.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistruct s3c_fb_win {
16662306a36Sopenharmony_ci	struct s3c_fb_pd_win	*windata;
16762306a36Sopenharmony_ci	struct s3c_fb		*parent;
16862306a36Sopenharmony_ci	struct fb_info		*fbinfo;
16962306a36Sopenharmony_ci	struct s3c_fb_palette	 palette;
17062306a36Sopenharmony_ci	struct s3c_fb_win_variant variant;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	u32			*palette_buffer;
17362306a36Sopenharmony_ci	u32			 pseudo_palette[16];
17462306a36Sopenharmony_ci	unsigned int		 index;
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/**
17862306a36Sopenharmony_ci * struct s3c_fb_vsync - vsync information
17962306a36Sopenharmony_ci * @wait:	a queue for processes waiting for vsync
18062306a36Sopenharmony_ci * @count:	vsync interrupt count
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_cistruct s3c_fb_vsync {
18362306a36Sopenharmony_ci	wait_queue_head_t	wait;
18462306a36Sopenharmony_ci	unsigned int		count;
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/**
18862306a36Sopenharmony_ci * struct s3c_fb - overall hardware state of the hardware
18962306a36Sopenharmony_ci * @slock: The spinlock protection for this data structure.
19062306a36Sopenharmony_ci * @dev: The device that we bound to, for printing, etc.
19162306a36Sopenharmony_ci * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
19262306a36Sopenharmony_ci * @lcd_clk: The clk (sclk) feeding pixclk.
19362306a36Sopenharmony_ci * @regs: The mapped hardware registers.
19462306a36Sopenharmony_ci * @variant: Variant information for this hardware.
19562306a36Sopenharmony_ci * @enabled: A bitmask of enabled hardware windows.
19662306a36Sopenharmony_ci * @output_on: Flag if the physical output is enabled.
19762306a36Sopenharmony_ci * @pdata: The platform configuration data passed with the device.
19862306a36Sopenharmony_ci * @windows: The hardware windows that have been claimed.
19962306a36Sopenharmony_ci * @irq_no: IRQ line number
20062306a36Sopenharmony_ci * @irq_flags: irq flags
20162306a36Sopenharmony_ci * @vsync_info: VSYNC-related information (count, queues...)
20262306a36Sopenharmony_ci */
20362306a36Sopenharmony_cistruct s3c_fb {
20462306a36Sopenharmony_ci	spinlock_t		slock;
20562306a36Sopenharmony_ci	struct device		*dev;
20662306a36Sopenharmony_ci	struct clk		*bus_clk;
20762306a36Sopenharmony_ci	struct clk		*lcd_clk;
20862306a36Sopenharmony_ci	void __iomem		*regs;
20962306a36Sopenharmony_ci	struct s3c_fb_variant	 variant;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	unsigned char		 enabled;
21262306a36Sopenharmony_ci	bool			 output_on;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	struct s3c_fb_platdata	*pdata;
21562306a36Sopenharmony_ci	struct s3c_fb_win	*windows[S3C_FB_MAX_WIN];
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	int			 irq_no;
21862306a36Sopenharmony_ci	unsigned long		 irq_flags;
21962306a36Sopenharmony_ci	struct s3c_fb_vsync	 vsync_info;
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/**
22362306a36Sopenharmony_ci * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode.
22462306a36Sopenharmony_ci * @win: The device window.
22562306a36Sopenharmony_ci * @bpp: The bit depth.
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_cistatic bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	return win->variant.valid_bpp & VALID_BPP(bpp);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/**
23362306a36Sopenharmony_ci * s3c_fb_check_var() - framebuffer layer request to verify a given mode.
23462306a36Sopenharmony_ci * @var: The screen information to verify.
23562306a36Sopenharmony_ci * @info: The framebuffer device.
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * Framebuffer layer call to verify the given information and allow us to
23862306a36Sopenharmony_ci * update various information depending on the hardware capabilities.
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_cistatic int s3c_fb_check_var(struct fb_var_screeninfo *var,
24162306a36Sopenharmony_ci			    struct fb_info *info)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct s3c_fb_win *win = info->par;
24462306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	dev_dbg(sfb->dev, "checking parameters\n");
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	var->xres_virtual = max(var->xres_virtual, var->xres);
24962306a36Sopenharmony_ci	var->yres_virtual = max(var->yres_virtual, var->yres);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {
25262306a36Sopenharmony_ci		dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",
25362306a36Sopenharmony_ci			win->index, var->bits_per_pixel);
25462306a36Sopenharmony_ci		return -EINVAL;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* always ensure these are zero, for drop through cases below */
25862306a36Sopenharmony_ci	var->transp.offset = 0;
25962306a36Sopenharmony_ci	var->transp.length = 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
26262306a36Sopenharmony_ci	case 1:
26362306a36Sopenharmony_ci	case 2:
26462306a36Sopenharmony_ci	case 4:
26562306a36Sopenharmony_ci	case 8:
26662306a36Sopenharmony_ci		if (sfb->variant.palette[win->index] != 0) {
26762306a36Sopenharmony_ci			/* non palletised, A:1,R:2,G:3,B:2 mode */
26862306a36Sopenharmony_ci			var->red.offset		= 5;
26962306a36Sopenharmony_ci			var->green.offset	= 2;
27062306a36Sopenharmony_ci			var->blue.offset	= 0;
27162306a36Sopenharmony_ci			var->red.length		= 2;
27262306a36Sopenharmony_ci			var->green.length	= 3;
27362306a36Sopenharmony_ci			var->blue.length	= 2;
27462306a36Sopenharmony_ci			var->transp.offset	= 7;
27562306a36Sopenharmony_ci			var->transp.length	= 1;
27662306a36Sopenharmony_ci		} else {
27762306a36Sopenharmony_ci			var->red.offset	= 0;
27862306a36Sopenharmony_ci			var->red.length	= var->bits_per_pixel;
27962306a36Sopenharmony_ci			var->green	= var->red;
28062306a36Sopenharmony_ci			var->blue	= var->red;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	case 19:
28562306a36Sopenharmony_ci		/* 666 with one bit alpha/transparency */
28662306a36Sopenharmony_ci		var->transp.offset	= 18;
28762306a36Sopenharmony_ci		var->transp.length	= 1;
28862306a36Sopenharmony_ci		fallthrough;
28962306a36Sopenharmony_ci	case 18:
29062306a36Sopenharmony_ci		var->bits_per_pixel	= 32;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		/* 666 format */
29362306a36Sopenharmony_ci		var->red.offset		= 12;
29462306a36Sopenharmony_ci		var->green.offset	= 6;
29562306a36Sopenharmony_ci		var->blue.offset	= 0;
29662306a36Sopenharmony_ci		var->red.length		= 6;
29762306a36Sopenharmony_ci		var->green.length	= 6;
29862306a36Sopenharmony_ci		var->blue.length	= 6;
29962306a36Sopenharmony_ci		break;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	case 16:
30262306a36Sopenharmony_ci		/* 16 bpp, 565 format */
30362306a36Sopenharmony_ci		var->red.offset		= 11;
30462306a36Sopenharmony_ci		var->green.offset	= 5;
30562306a36Sopenharmony_ci		var->blue.offset	= 0;
30662306a36Sopenharmony_ci		var->red.length		= 5;
30762306a36Sopenharmony_ci		var->green.length	= 6;
30862306a36Sopenharmony_ci		var->blue.length	= 5;
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	case 32:
31262306a36Sopenharmony_ci	case 28:
31362306a36Sopenharmony_ci	case 25:
31462306a36Sopenharmony_ci		var->transp.length	= var->bits_per_pixel - 24;
31562306a36Sopenharmony_ci		var->transp.offset	= 24;
31662306a36Sopenharmony_ci		fallthrough;
31762306a36Sopenharmony_ci	case 24:
31862306a36Sopenharmony_ci		/* our 24bpp is unpacked, so 32bpp */
31962306a36Sopenharmony_ci		var->bits_per_pixel	= 32;
32062306a36Sopenharmony_ci		var->red.offset		= 16;
32162306a36Sopenharmony_ci		var->red.length		= 8;
32262306a36Sopenharmony_ci		var->green.offset	= 8;
32362306a36Sopenharmony_ci		var->green.length	= 8;
32462306a36Sopenharmony_ci		var->blue.offset	= 0;
32562306a36Sopenharmony_ci		var->blue.length	= 8;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	default:
32962306a36Sopenharmony_ci		dev_err(sfb->dev, "invalid bpp\n");
33062306a36Sopenharmony_ci		return -EINVAL;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	dev_dbg(sfb->dev, "%s: verified parameters\n", __func__);
33462306a36Sopenharmony_ci	return 0;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/**
33862306a36Sopenharmony_ci * s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock.
33962306a36Sopenharmony_ci * @sfb: The hardware state.
34062306a36Sopenharmony_ci * @pixclk: The pixel clock wanted, in picoseconds.
34162306a36Sopenharmony_ci *
34262306a36Sopenharmony_ci * Given the specified pixel clock, work out the necessary divider to get
34362306a36Sopenharmony_ci * close to the output frequency.
34462306a36Sopenharmony_ci */
34562306a36Sopenharmony_cistatic int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	unsigned long clk;
34862306a36Sopenharmony_ci	unsigned long long tmp;
34962306a36Sopenharmony_ci	unsigned int result;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (sfb->variant.has_clksel)
35262306a36Sopenharmony_ci		clk = clk_get_rate(sfb->bus_clk);
35362306a36Sopenharmony_ci	else
35462306a36Sopenharmony_ci		clk = clk_get_rate(sfb->lcd_clk);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	tmp = (unsigned long long)clk;
35762306a36Sopenharmony_ci	tmp *= pixclk;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	do_div(tmp, 1000000000UL);
36062306a36Sopenharmony_ci	result = (unsigned int)tmp / 1000;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n",
36362306a36Sopenharmony_ci		pixclk, clk, result, result ? clk / result : clk);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return result;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/**
36962306a36Sopenharmony_ci * s3c_fb_align_word() - align pixel count to word boundary
37062306a36Sopenharmony_ci * @bpp: The number of bits per pixel
37162306a36Sopenharmony_ci * @pix: The value to be aligned.
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * Align the given pixel count so that it will start on an 32bit word
37462306a36Sopenharmony_ci * boundary.
37562306a36Sopenharmony_ci */
37662306a36Sopenharmony_cistatic int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	int pix_per_word;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (bpp > 16)
38162306a36Sopenharmony_ci		return pix;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	pix_per_word = (8 * 32) / bpp;
38462306a36Sopenharmony_ci	return ALIGN(pix, pix_per_word);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/**
38862306a36Sopenharmony_ci * vidosd_set_size() - set OSD size for a window
38962306a36Sopenharmony_ci *
39062306a36Sopenharmony_ci * @win: the window to set OSD size for
39162306a36Sopenharmony_ci * @size: OSD size register value
39262306a36Sopenharmony_ci */
39362306a36Sopenharmony_cistatic void vidosd_set_size(struct s3c_fb_win *win, u32 size)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* OSD can be set up if osd_size_off != 0 for this window */
39862306a36Sopenharmony_ci	if (win->variant.osd_size_off)
39962306a36Sopenharmony_ci		writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)
40062306a36Sopenharmony_ci				+ win->variant.osd_size_off);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci/**
40462306a36Sopenharmony_ci * vidosd_set_alpha() - set alpha transparency for a window
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * @win: the window to set OSD size for
40762306a36Sopenharmony_ci * @alpha: alpha register value
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_cistatic void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (win->variant.has_osd_alpha)
41462306a36Sopenharmony_ci		writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant));
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/**
41862306a36Sopenharmony_ci * shadow_protect_win() - disable updating values from shadow registers at vsync
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * @win: window to protect registers for
42162306a36Sopenharmony_ci * @protect: 1 to protect (disable updates)
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_cistatic void shadow_protect_win(struct s3c_fb_win *win, bool protect)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
42662306a36Sopenharmony_ci	u32 reg;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (protect) {
42962306a36Sopenharmony_ci		if (sfb->variant.has_prtcon) {
43062306a36Sopenharmony_ci			writel(PRTCON_PROTECT, sfb->regs + PRTCON);
43162306a36Sopenharmony_ci		} else if (sfb->variant.has_shadowcon) {
43262306a36Sopenharmony_ci			reg = readl(sfb->regs + SHADOWCON);
43362306a36Sopenharmony_ci			writel(reg | SHADOWCON_WINx_PROTECT(win->index),
43462306a36Sopenharmony_ci				sfb->regs + SHADOWCON);
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci	} else {
43762306a36Sopenharmony_ci		if (sfb->variant.has_prtcon) {
43862306a36Sopenharmony_ci			writel(0, sfb->regs + PRTCON);
43962306a36Sopenharmony_ci		} else if (sfb->variant.has_shadowcon) {
44062306a36Sopenharmony_ci			reg = readl(sfb->regs + SHADOWCON);
44162306a36Sopenharmony_ci			writel(reg & ~SHADOWCON_WINx_PROTECT(win->index),
44262306a36Sopenharmony_ci				sfb->regs + SHADOWCON);
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci/**
44862306a36Sopenharmony_ci * s3c_fb_enable() - Set the state of the main LCD output
44962306a36Sopenharmony_ci * @sfb: The main framebuffer state.
45062306a36Sopenharmony_ci * @enable: The state to set.
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_cistatic void s3c_fb_enable(struct s3c_fb *sfb, int enable)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	u32 vidcon0 = readl(sfb->regs + VIDCON0);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (enable && !sfb->output_on)
45762306a36Sopenharmony_ci		pm_runtime_get_sync(sfb->dev);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (enable) {
46062306a36Sopenharmony_ci		vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;
46162306a36Sopenharmony_ci	} else {
46262306a36Sopenharmony_ci		/* see the note in the framebuffer datasheet about
46362306a36Sopenharmony_ci		 * why you cannot take both of these bits down at the
46462306a36Sopenharmony_ci		 * same time. */
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		if (vidcon0 & VIDCON0_ENVID) {
46762306a36Sopenharmony_ci			vidcon0 |= VIDCON0_ENVID;
46862306a36Sopenharmony_ci			vidcon0 &= ~VIDCON0_ENVID_F;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	writel(vidcon0, sfb->regs + VIDCON0);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (!enable && sfb->output_on)
47562306a36Sopenharmony_ci		pm_runtime_put_sync(sfb->dev);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	sfb->output_on = enable;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci/**
48162306a36Sopenharmony_ci * s3c_fb_set_par() - framebuffer request to set new framebuffer state.
48262306a36Sopenharmony_ci * @info: The framebuffer to change.
48362306a36Sopenharmony_ci *
48462306a36Sopenharmony_ci * Framebuffer layer request to set a new mode for the specified framebuffer
48562306a36Sopenharmony_ci */
48662306a36Sopenharmony_cistatic int s3c_fb_set_par(struct fb_info *info)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
48962306a36Sopenharmony_ci	struct s3c_fb_win *win = info->par;
49062306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
49162306a36Sopenharmony_ci	void __iomem *regs = sfb->regs;
49262306a36Sopenharmony_ci	void __iomem *buf;
49362306a36Sopenharmony_ci	int win_no = win->index;
49462306a36Sopenharmony_ci	u32 alpha = 0;
49562306a36Sopenharmony_ci	u32 data;
49662306a36Sopenharmony_ci	u32 pagewidth;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	dev_dbg(sfb->dev, "setting framebuffer parameters\n");
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	shadow_protect_win(win, 1);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
50562306a36Sopenharmony_ci	case 32:
50662306a36Sopenharmony_ci	case 24:
50762306a36Sopenharmony_ci	case 16:
50862306a36Sopenharmony_ci	case 12:
50962306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
51062306a36Sopenharmony_ci		break;
51162306a36Sopenharmony_ci	case 8:
51262306a36Sopenharmony_ci		if (win->variant.palette_sz >= 256)
51362306a36Sopenharmony_ci			info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
51462306a36Sopenharmony_ci		else
51562306a36Sopenharmony_ci			info->fix.visual = FB_VISUAL_TRUECOLOR;
51662306a36Sopenharmony_ci		break;
51762306a36Sopenharmony_ci	case 1:
51862306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_MONO01;
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci	default:
52162306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
52862306a36Sopenharmony_ci	info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* disable the window whilst we update it */
53162306a36Sopenharmony_ci	writel(0, regs + WINCON(win_no));
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (!sfb->output_on)
53462306a36Sopenharmony_ci		s3c_fb_enable(sfb, 1);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* write the buffer address */
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* start and end registers stride is 8 */
53962306a36Sopenharmony_ci	buf = regs + win_no * 8;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	writel(info->fix.smem_start, buf + sfb->variant.buf_start);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	data = info->fix.smem_start + info->fix.line_length * var->yres;
54462306a36Sopenharmony_ci	writel(data, buf + sfb->variant.buf_end);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	pagewidth = (var->xres * var->bits_per_pixel) >> 3;
54762306a36Sopenharmony_ci	data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
54862306a36Sopenharmony_ci	       VIDW_BUF_SIZE_PAGEWIDTH(pagewidth) |
54962306a36Sopenharmony_ci	       VIDW_BUF_SIZE_OFFSET_E(info->fix.line_length - pagewidth) |
55062306a36Sopenharmony_ci	       VIDW_BUF_SIZE_PAGEWIDTH_E(pagewidth);
55162306a36Sopenharmony_ci	writel(data, regs + sfb->variant.buf_size + (win_no * 4));
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* write 'OSD' registers to control position of framebuffer */
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0) |
55662306a36Sopenharmony_ci	       VIDOSDxA_TOPLEFT_X_E(0) | VIDOSDxA_TOPLEFT_Y_E(0);
55762306a36Sopenharmony_ci	writel(data, regs + VIDOSD_A(win_no, sfb->variant));
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
56062306a36Sopenharmony_ci						     var->xres - 1)) |
56162306a36Sopenharmony_ci	       VIDOSDxB_BOTRIGHT_Y(var->yres - 1) |
56262306a36Sopenharmony_ci	       VIDOSDxB_BOTRIGHT_X_E(s3c_fb_align_word(var->bits_per_pixel,
56362306a36Sopenharmony_ci						     var->xres - 1)) |
56462306a36Sopenharmony_ci	       VIDOSDxB_BOTRIGHT_Y_E(var->yres - 1);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	writel(data, regs + VIDOSD_B(win_no, sfb->variant));
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	data = var->xres * var->yres;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	alpha = VIDISD14C_ALPHA1_R(0xf) |
57162306a36Sopenharmony_ci		VIDISD14C_ALPHA1_G(0xf) |
57262306a36Sopenharmony_ci		VIDISD14C_ALPHA1_B(0xf);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	vidosd_set_alpha(win, alpha);
57562306a36Sopenharmony_ci	vidosd_set_size(win, data);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* Enable DMA channel for this window */
57862306a36Sopenharmony_ci	if (sfb->variant.has_shadowcon) {
57962306a36Sopenharmony_ci		data = readl(sfb->regs + SHADOWCON);
58062306a36Sopenharmony_ci		data |= SHADOWCON_CHx_ENABLE(win_no);
58162306a36Sopenharmony_ci		writel(data, sfb->regs + SHADOWCON);
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	data = WINCONx_ENWIN;
58562306a36Sopenharmony_ci	sfb->enabled |= (1 << win->index);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/* note, since we have to round up the bits-per-pixel, we end up
58862306a36Sopenharmony_ci	 * relying on the bitfield information for r/g/b/a to work out
58962306a36Sopenharmony_ci	 * exactly which mode of operation is intended. */
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
59262306a36Sopenharmony_ci	case 1:
59362306a36Sopenharmony_ci		data |= WINCON0_BPPMODE_1BPP;
59462306a36Sopenharmony_ci		data |= WINCONx_BITSWP;
59562306a36Sopenharmony_ci		data |= WINCONx_BURSTLEN_4WORD;
59662306a36Sopenharmony_ci		break;
59762306a36Sopenharmony_ci	case 2:
59862306a36Sopenharmony_ci		data |= WINCON0_BPPMODE_2BPP;
59962306a36Sopenharmony_ci		data |= WINCONx_BITSWP;
60062306a36Sopenharmony_ci		data |= WINCONx_BURSTLEN_8WORD;
60162306a36Sopenharmony_ci		break;
60262306a36Sopenharmony_ci	case 4:
60362306a36Sopenharmony_ci		data |= WINCON0_BPPMODE_4BPP;
60462306a36Sopenharmony_ci		data |= WINCONx_BITSWP;
60562306a36Sopenharmony_ci		data |= WINCONx_BURSTLEN_8WORD;
60662306a36Sopenharmony_ci		break;
60762306a36Sopenharmony_ci	case 8:
60862306a36Sopenharmony_ci		if (var->transp.length != 0)
60962306a36Sopenharmony_ci			data |= WINCON1_BPPMODE_8BPP_1232;
61062306a36Sopenharmony_ci		else
61162306a36Sopenharmony_ci			data |= WINCON0_BPPMODE_8BPP_PALETTE;
61262306a36Sopenharmony_ci		data |= WINCONx_BURSTLEN_8WORD;
61362306a36Sopenharmony_ci		data |= WINCONx_BYTSWP;
61462306a36Sopenharmony_ci		break;
61562306a36Sopenharmony_ci	case 16:
61662306a36Sopenharmony_ci		if (var->transp.length != 0)
61762306a36Sopenharmony_ci			data |= WINCON1_BPPMODE_16BPP_A1555;
61862306a36Sopenharmony_ci		else
61962306a36Sopenharmony_ci			data |= WINCON0_BPPMODE_16BPP_565;
62062306a36Sopenharmony_ci		data |= WINCONx_HAWSWP;
62162306a36Sopenharmony_ci		data |= WINCONx_BURSTLEN_16WORD;
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci	case 24:
62462306a36Sopenharmony_ci	case 32:
62562306a36Sopenharmony_ci		if (var->red.length == 6) {
62662306a36Sopenharmony_ci			if (var->transp.length != 0)
62762306a36Sopenharmony_ci				data |= WINCON1_BPPMODE_19BPP_A1666;
62862306a36Sopenharmony_ci			else
62962306a36Sopenharmony_ci				data |= WINCON1_BPPMODE_18BPP_666;
63062306a36Sopenharmony_ci		} else if (var->transp.length == 1)
63162306a36Sopenharmony_ci			data |= WINCON1_BPPMODE_25BPP_A1888
63262306a36Sopenharmony_ci				| WINCON1_BLD_PIX;
63362306a36Sopenharmony_ci		else if ((var->transp.length == 4) ||
63462306a36Sopenharmony_ci			(var->transp.length == 8))
63562306a36Sopenharmony_ci			data |= WINCON1_BPPMODE_28BPP_A4888
63662306a36Sopenharmony_ci				| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
63762306a36Sopenharmony_ci		else
63862306a36Sopenharmony_ci			data |= WINCON0_BPPMODE_24BPP_888;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		data |= WINCONx_WSWP;
64162306a36Sopenharmony_ci		data |= WINCONx_BURSTLEN_16WORD;
64262306a36Sopenharmony_ci		break;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* Enable the colour keying for the window below this one */
64662306a36Sopenharmony_ci	if (win_no > 0) {
64762306a36Sopenharmony_ci		u32 keycon0_data = 0, keycon1_data = 0;
64862306a36Sopenharmony_ci		void __iomem *keycon = regs + sfb->variant.keycon;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		keycon0_data = ~(WxKEYCON0_KEYBL_EN |
65162306a36Sopenharmony_ci				WxKEYCON0_KEYEN_F |
65262306a36Sopenharmony_ci				WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		keycon1_data = WxKEYCON1_COLVAL(0xffffff);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		keycon += (win_no - 1) * 8;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		writel(keycon0_data, keycon + WKEYCON0);
65962306a36Sopenharmony_ci		writel(keycon1_data, keycon + WKEYCON1);
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	writel(data, regs + sfb->variant.wincon + (win_no * 4));
66362306a36Sopenharmony_ci	writel(0x0, regs + sfb->variant.winmap + (win_no * 4));
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/* Set alpha value width */
66662306a36Sopenharmony_ci	if (sfb->variant.has_blendcon) {
66762306a36Sopenharmony_ci		data = readl(sfb->regs + BLENDCON);
66862306a36Sopenharmony_ci		data &= ~BLENDCON_NEW_MASK;
66962306a36Sopenharmony_ci		if (var->transp.length > 4)
67062306a36Sopenharmony_ci			data |= BLENDCON_NEW_8BIT_ALPHA_VALUE;
67162306a36Sopenharmony_ci		else
67262306a36Sopenharmony_ci			data |= BLENDCON_NEW_4BIT_ALPHA_VALUE;
67362306a36Sopenharmony_ci		writel(data, sfb->regs + BLENDCON);
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	shadow_protect_win(win, 0);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci/**
68462306a36Sopenharmony_ci * s3c_fb_update_palette() - set or schedule a palette update.
68562306a36Sopenharmony_ci * @sfb: The hardware information.
68662306a36Sopenharmony_ci * @win: The window being updated.
68762306a36Sopenharmony_ci * @reg: The palette index being changed.
68862306a36Sopenharmony_ci * @value: The computed palette value.
68962306a36Sopenharmony_ci *
69062306a36Sopenharmony_ci * Change the value of a palette register, either by directly writing to
69162306a36Sopenharmony_ci * the palette (this requires the palette RAM to be disconnected from the
69262306a36Sopenharmony_ci * hardware whilst this is in progress) or schedule the update for later.
69362306a36Sopenharmony_ci *
69462306a36Sopenharmony_ci * At the moment, since we have no VSYNC interrupt support, we simply set
69562306a36Sopenharmony_ci * the palette entry directly.
69662306a36Sopenharmony_ci */
69762306a36Sopenharmony_cistatic void s3c_fb_update_palette(struct s3c_fb *sfb,
69862306a36Sopenharmony_ci				  struct s3c_fb_win *win,
69962306a36Sopenharmony_ci				  unsigned int reg,
70062306a36Sopenharmony_ci				  u32 value)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	void __iomem *palreg;
70362306a36Sopenharmony_ci	u32 palcon;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	palreg = sfb->regs + sfb->variant.palette[win->index];
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",
70862306a36Sopenharmony_ci		__func__, win->index, reg, palreg, value);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	win->palette_buffer[reg] = value;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	palcon = readl(sfb->regs + WPALCON);
71362306a36Sopenharmony_ci	writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (win->variant.palette_16bpp)
71662306a36Sopenharmony_ci		writew(value, palreg + (reg * 2));
71762306a36Sopenharmony_ci	else
71862306a36Sopenharmony_ci		writel(value, palreg + (reg * 4));
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	writel(palcon, sfb->regs + WPALCON);
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic inline unsigned int chan_to_field(unsigned int chan,
72462306a36Sopenharmony_ci					 struct fb_bitfield *bf)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	chan &= 0xffff;
72762306a36Sopenharmony_ci	chan >>= 16 - bf->length;
72862306a36Sopenharmony_ci	return chan << bf->offset;
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/**
73262306a36Sopenharmony_ci * s3c_fb_setcolreg() - framebuffer layer request to change palette.
73362306a36Sopenharmony_ci * @regno: The palette index to change.
73462306a36Sopenharmony_ci * @red: The red field for the palette data.
73562306a36Sopenharmony_ci * @green: The green field for the palette data.
73662306a36Sopenharmony_ci * @blue: The blue field for the palette data.
73762306a36Sopenharmony_ci * @transp: The transparency (alpha) field for the palette data.
73862306a36Sopenharmony_ci * @info: The framebuffer being changed.
73962306a36Sopenharmony_ci */
74062306a36Sopenharmony_cistatic int s3c_fb_setcolreg(unsigned regno,
74162306a36Sopenharmony_ci			    unsigned red, unsigned green, unsigned blue,
74262306a36Sopenharmony_ci			    unsigned transp, struct fb_info *info)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct s3c_fb_win *win = info->par;
74562306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
74662306a36Sopenharmony_ci	unsigned int val;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n",
74962306a36Sopenharmony_ci		__func__, win->index, regno, red, green, blue);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	switch (info->fix.visual) {
75462306a36Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
75562306a36Sopenharmony_ci		/* true-colour, use pseudo-palette */
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		if (regno < 16) {
75862306a36Sopenharmony_ci			u32 *pal = info->pseudo_palette;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci			val  = chan_to_field(red,   &info->var.red);
76162306a36Sopenharmony_ci			val |= chan_to_field(green, &info->var.green);
76262306a36Sopenharmony_ci			val |= chan_to_field(blue,  &info->var.blue);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci			pal[regno] = val;
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci		break;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	case FB_VISUAL_PSEUDOCOLOR:
76962306a36Sopenharmony_ci		if (regno < win->variant.palette_sz) {
77062306a36Sopenharmony_ci			val  = chan_to_field(red, &win->palette.r);
77162306a36Sopenharmony_ci			val |= chan_to_field(green, &win->palette.g);
77262306a36Sopenharmony_ci			val |= chan_to_field(blue, &win->palette.b);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci			s3c_fb_update_palette(sfb, win, regno, val);
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		break;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	default:
78062306a36Sopenharmony_ci		pm_runtime_put_sync(sfb->dev);
78162306a36Sopenharmony_ci		return 1;	/* unknown type */
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
78562306a36Sopenharmony_ci	return 0;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci/**
78962306a36Sopenharmony_ci * s3c_fb_blank() - blank or unblank the given window
79062306a36Sopenharmony_ci * @blank_mode: The blank state from FB_BLANK_*
79162306a36Sopenharmony_ci * @info: The framebuffer to blank.
79262306a36Sopenharmony_ci *
79362306a36Sopenharmony_ci * Framebuffer layer request to change the power state.
79462306a36Sopenharmony_ci */
79562306a36Sopenharmony_cistatic int s3c_fb_blank(int blank_mode, struct fb_info *info)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	struct s3c_fb_win *win = info->par;
79862306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
79962306a36Sopenharmony_ci	unsigned int index = win->index;
80062306a36Sopenharmony_ci	u32 wincon;
80162306a36Sopenharmony_ci	u32 output_on = sfb->output_on;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4));
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	switch (blank_mode) {
81062306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
81162306a36Sopenharmony_ci		wincon &= ~WINCONx_ENWIN;
81262306a36Sopenharmony_ci		sfb->enabled &= ~(1 << index);
81362306a36Sopenharmony_ci		fallthrough;	/* to FB_BLANK_NORMAL */
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
81662306a36Sopenharmony_ci		/* disable the DMA and display 0x0 (black) */
81762306a36Sopenharmony_ci		shadow_protect_win(win, 1);
81862306a36Sopenharmony_ci		writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
81962306a36Sopenharmony_ci		       sfb->regs + sfb->variant.winmap + (index * 4));
82062306a36Sopenharmony_ci		shadow_protect_win(win, 0);
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
82462306a36Sopenharmony_ci		shadow_protect_win(win, 1);
82562306a36Sopenharmony_ci		writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4));
82662306a36Sopenharmony_ci		shadow_protect_win(win, 0);
82762306a36Sopenharmony_ci		wincon |= WINCONx_ENWIN;
82862306a36Sopenharmony_ci		sfb->enabled |= (1 << index);
82962306a36Sopenharmony_ci		break;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
83262306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
83362306a36Sopenharmony_ci	default:
83462306a36Sopenharmony_ci		pm_runtime_put_sync(sfb->dev);
83562306a36Sopenharmony_ci		return 1;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	shadow_protect_win(win, 1);
83962306a36Sopenharmony_ci	writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4));
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* Check the enabled state to see if we need to be running the
84262306a36Sopenharmony_ci	 * main LCD interface, as if there are no active windows then
84362306a36Sopenharmony_ci	 * it is highly likely that we also do not need to output
84462306a36Sopenharmony_ci	 * anything.
84562306a36Sopenharmony_ci	 */
84662306a36Sopenharmony_ci	s3c_fb_enable(sfb, sfb->enabled ? 1 : 0);
84762306a36Sopenharmony_ci	shadow_protect_win(win, 0);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return output_on == sfb->output_on;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci/**
85562306a36Sopenharmony_ci * s3c_fb_pan_display() - Pan the display.
85662306a36Sopenharmony_ci *
85762306a36Sopenharmony_ci * Note that the offsets can be written to the device at any time, as their
85862306a36Sopenharmony_ci * values are latched at each vsync automatically. This also means that only
85962306a36Sopenharmony_ci * the last call to this function will have any effect on next vsync, but
86062306a36Sopenharmony_ci * there is no need to sleep waiting for it to prevent tearing.
86162306a36Sopenharmony_ci *
86262306a36Sopenharmony_ci * @var: The screen information to verify.
86362306a36Sopenharmony_ci * @info: The framebuffer device.
86462306a36Sopenharmony_ci */
86562306a36Sopenharmony_cistatic int s3c_fb_pan_display(struct fb_var_screeninfo *var,
86662306a36Sopenharmony_ci			      struct fb_info *info)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct s3c_fb_win *win	= info->par;
86962306a36Sopenharmony_ci	struct s3c_fb *sfb	= win->parent;
87062306a36Sopenharmony_ci	void __iomem *buf	= sfb->regs + win->index * 8;
87162306a36Sopenharmony_ci	unsigned int start_boff, end_boff;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	/* Offset in bytes to the start of the displayed area */
87662306a36Sopenharmony_ci	start_boff = var->yoffset * info->fix.line_length;
87762306a36Sopenharmony_ci	/* X offset depends on the current bpp */
87862306a36Sopenharmony_ci	if (info->var.bits_per_pixel >= 8) {
87962306a36Sopenharmony_ci		start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);
88062306a36Sopenharmony_ci	} else {
88162306a36Sopenharmony_ci		switch (info->var.bits_per_pixel) {
88262306a36Sopenharmony_ci		case 4:
88362306a36Sopenharmony_ci			start_boff += var->xoffset >> 1;
88462306a36Sopenharmony_ci			break;
88562306a36Sopenharmony_ci		case 2:
88662306a36Sopenharmony_ci			start_boff += var->xoffset >> 2;
88762306a36Sopenharmony_ci			break;
88862306a36Sopenharmony_ci		case 1:
88962306a36Sopenharmony_ci			start_boff += var->xoffset >> 3;
89062306a36Sopenharmony_ci			break;
89162306a36Sopenharmony_ci		default:
89262306a36Sopenharmony_ci			dev_err(sfb->dev, "invalid bpp\n");
89362306a36Sopenharmony_ci			pm_runtime_put_sync(sfb->dev);
89462306a36Sopenharmony_ci			return -EINVAL;
89562306a36Sopenharmony_ci		}
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci	/* Offset in bytes to the end of the displayed area */
89862306a36Sopenharmony_ci	end_boff = start_boff + info->var.yres * info->fix.line_length;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* Temporarily turn off per-vsync update from shadow registers until
90162306a36Sopenharmony_ci	 * both start and end addresses are updated to prevent corruption */
90262306a36Sopenharmony_ci	shadow_protect_win(win, 1);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start);
90562306a36Sopenharmony_ci	writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	shadow_protect_win(win, 0);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
91062306a36Sopenharmony_ci	return 0;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci/**
91462306a36Sopenharmony_ci * s3c_fb_enable_irq() - enable framebuffer interrupts
91562306a36Sopenharmony_ci * @sfb: main hardware state
91662306a36Sopenharmony_ci */
91762306a36Sopenharmony_cistatic void s3c_fb_enable_irq(struct s3c_fb *sfb)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	void __iomem *regs = sfb->regs;
92062306a36Sopenharmony_ci	u32 irq_ctrl_reg;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
92362306a36Sopenharmony_ci		/* IRQ disabled, enable it */
92462306a36Sopenharmony_ci		irq_ctrl_reg = readl(regs + VIDINTCON0);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;
92762306a36Sopenharmony_ci		irq_ctrl_reg |= VIDINTCON0_INT_FRAME;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;
93062306a36Sopenharmony_ci		irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;
93162306a36Sopenharmony_ci		irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;
93262306a36Sopenharmony_ci		irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		writel(irq_ctrl_reg, regs + VIDINTCON0);
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci/**
93962306a36Sopenharmony_ci * s3c_fb_disable_irq() - disable framebuffer interrupts
94062306a36Sopenharmony_ci * @sfb: main hardware state
94162306a36Sopenharmony_ci */
94262306a36Sopenharmony_cistatic void s3c_fb_disable_irq(struct s3c_fb *sfb)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	void __iomem *regs = sfb->regs;
94562306a36Sopenharmony_ci	u32 irq_ctrl_reg;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
94862306a36Sopenharmony_ci		/* IRQ enabled, disable it */
94962306a36Sopenharmony_ci		irq_ctrl_reg = readl(regs + VIDINTCON0);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;
95262306a36Sopenharmony_ci		irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		writel(irq_ctrl_reg, regs + VIDINTCON0);
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic irqreturn_t s3c_fb_irq(int irq, void *dev_id)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct s3c_fb *sfb = dev_id;
96162306a36Sopenharmony_ci	void __iomem  *regs = sfb->regs;
96262306a36Sopenharmony_ci	u32 irq_sts_reg;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	spin_lock(&sfb->slock);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	irq_sts_reg = readl(regs + VIDINTCON1);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (irq_sts_reg & VIDINTCON1_INT_FRAME) {
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		/* VSYNC interrupt, accept it */
97162306a36Sopenharmony_ci		writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci		sfb->vsync_info.count++;
97462306a36Sopenharmony_ci		wake_up_interruptible(&sfb->vsync_info.wait);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/* We only support waiting for VSYNC for now, so it's safe
97862306a36Sopenharmony_ci	 * to always disable irqs here.
97962306a36Sopenharmony_ci	 */
98062306a36Sopenharmony_ci	s3c_fb_disable_irq(sfb);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	spin_unlock(&sfb->slock);
98362306a36Sopenharmony_ci	return IRQ_HANDLED;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci/**
98762306a36Sopenharmony_ci * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout
98862306a36Sopenharmony_ci * @sfb: main hardware state
98962306a36Sopenharmony_ci * @crtc: head index.
99062306a36Sopenharmony_ci */
99162306a36Sopenharmony_cistatic int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	unsigned long count;
99462306a36Sopenharmony_ci	int ret;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (crtc != 0)
99762306a36Sopenharmony_ci		return -ENODEV;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	count = sfb->vsync_info.count;
100262306a36Sopenharmony_ci	s3c_fb_enable_irq(sfb);
100362306a36Sopenharmony_ci	ret = wait_event_interruptible_timeout(sfb->vsync_info.wait,
100462306a36Sopenharmony_ci				       count != sfb->vsync_info.count,
100562306a36Sopenharmony_ci				       msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (ret == 0)
101062306a36Sopenharmony_ci		return -ETIMEDOUT;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	return 0;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,
101662306a36Sopenharmony_ci			unsigned long arg)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	struct s3c_fb_win *win = info->par;
101962306a36Sopenharmony_ci	struct s3c_fb *sfb = win->parent;
102062306a36Sopenharmony_ci	int ret;
102162306a36Sopenharmony_ci	u32 crtc;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	switch (cmd) {
102462306a36Sopenharmony_ci	case FBIO_WAITFORVSYNC:
102562306a36Sopenharmony_ci		if (get_user(crtc, (u32 __user *)arg)) {
102662306a36Sopenharmony_ci			ret = -EFAULT;
102762306a36Sopenharmony_ci			break;
102862306a36Sopenharmony_ci		}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci		ret = s3c_fb_wait_for_vsync(sfb, crtc);
103162306a36Sopenharmony_ci		break;
103262306a36Sopenharmony_ci	default:
103362306a36Sopenharmony_ci		ret = -ENOTTY;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	return ret;
103762306a36Sopenharmony_ci}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cistatic const struct fb_ops s3c_fb_ops = {
104062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
104162306a36Sopenharmony_ci	FB_DEFAULT_IOMEM_OPS,
104262306a36Sopenharmony_ci	.fb_check_var	= s3c_fb_check_var,
104362306a36Sopenharmony_ci	.fb_set_par	= s3c_fb_set_par,
104462306a36Sopenharmony_ci	.fb_blank	= s3c_fb_blank,
104562306a36Sopenharmony_ci	.fb_setcolreg	= s3c_fb_setcolreg,
104662306a36Sopenharmony_ci	.fb_pan_display	= s3c_fb_pan_display,
104762306a36Sopenharmony_ci	.fb_ioctl	= s3c_fb_ioctl,
104862306a36Sopenharmony_ci};
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci/**
105162306a36Sopenharmony_ci * s3c_fb_missing_pixclock() - calculates pixel clock
105262306a36Sopenharmony_ci * @mode: The video mode to change.
105362306a36Sopenharmony_ci *
105462306a36Sopenharmony_ci * Calculate the pixel clock when none has been given through platform data.
105562306a36Sopenharmony_ci */
105662306a36Sopenharmony_cistatic void s3c_fb_missing_pixclock(struct fb_videomode *mode)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	u64 pixclk = 1000000000000ULL;
105962306a36Sopenharmony_ci	u32 div;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	div  = mode->left_margin + mode->hsync_len + mode->right_margin +
106262306a36Sopenharmony_ci	       mode->xres;
106362306a36Sopenharmony_ci	div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +
106462306a36Sopenharmony_ci	       mode->yres;
106562306a36Sopenharmony_ci	div *= mode->refresh ? : 60;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	do_div(pixclk, div);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	mode->pixclock = pixclk;
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci/**
107362306a36Sopenharmony_ci * s3c_fb_alloc_memory() - allocate display memory for framebuffer window
107462306a36Sopenharmony_ci * @sfb: The base resources for the hardware.
107562306a36Sopenharmony_ci * @win: The window to initialise memory for.
107662306a36Sopenharmony_ci *
107762306a36Sopenharmony_ci * Allocate memory for the given framebuffer.
107862306a36Sopenharmony_ci */
107962306a36Sopenharmony_cistatic int s3c_fb_alloc_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	struct s3c_fb_pd_win *windata = win->windata;
108262306a36Sopenharmony_ci	unsigned int real_size, virt_size, size;
108362306a36Sopenharmony_ci	struct fb_info *fbi = win->fbinfo;
108462306a36Sopenharmony_ci	dma_addr_t map_dma;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	dev_dbg(sfb->dev, "allocating memory for display\n");
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	real_size = windata->xres * windata->yres;
108962306a36Sopenharmony_ci	virt_size = windata->virtual_x * windata->virtual_y;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",
109262306a36Sopenharmony_ci		real_size, windata->xres, windata->yres,
109362306a36Sopenharmony_ci		virt_size, windata->virtual_x, windata->virtual_y);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	size = (real_size > virt_size) ? real_size : virt_size;
109662306a36Sopenharmony_ci	size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;
109762306a36Sopenharmony_ci	size /= 8;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	fbi->fix.smem_len = size;
110062306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	dev_dbg(sfb->dev, "want %u bytes for window\n", size);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	fbi->screen_buffer = dma_alloc_wc(sfb->dev, size, &map_dma, GFP_KERNEL);
110562306a36Sopenharmony_ci	if (!fbi->screen_buffer)
110662306a36Sopenharmony_ci		return -ENOMEM;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	dev_dbg(sfb->dev, "mapped %x to %p\n",
110962306a36Sopenharmony_ci		(unsigned int)map_dma, fbi->screen_buffer);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	memset(fbi->screen_buffer, 0x0, size);
111262306a36Sopenharmony_ci	fbi->fix.smem_start = map_dma;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci/**
111862306a36Sopenharmony_ci * s3c_fb_free_memory() - free the display memory for the given window
111962306a36Sopenharmony_ci * @sfb: The base resources for the hardware.
112062306a36Sopenharmony_ci * @win: The window to free the display memory for.
112162306a36Sopenharmony_ci *
112262306a36Sopenharmony_ci * Free the display memory allocated by s3c_fb_alloc_memory().
112362306a36Sopenharmony_ci */
112462306a36Sopenharmony_cistatic void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	struct fb_info *fbi = win->fbinfo;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	if (fbi->screen_buffer)
112962306a36Sopenharmony_ci		dma_free_wc(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
113062306a36Sopenharmony_ci			    fbi->screen_buffer, fbi->fix.smem_start);
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci/**
113462306a36Sopenharmony_ci * s3c_fb_release_win() - release resources for a framebuffer window.
113562306a36Sopenharmony_ci * @sfb: The base resources for the hardware.
113662306a36Sopenharmony_ci * @win: The window to cleanup the resources for.
113762306a36Sopenharmony_ci *
113862306a36Sopenharmony_ci * Release the resources that where claimed for the hardware window,
113962306a36Sopenharmony_ci * such as the framebuffer instance and any memory claimed for it.
114062306a36Sopenharmony_ci */
114162306a36Sopenharmony_cistatic void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	u32 data;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (win->fbinfo) {
114662306a36Sopenharmony_ci		if (sfb->variant.has_shadowcon) {
114762306a36Sopenharmony_ci			data = readl(sfb->regs + SHADOWCON);
114862306a36Sopenharmony_ci			data &= ~SHADOWCON_CHx_ENABLE(win->index);
114962306a36Sopenharmony_ci			data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index);
115062306a36Sopenharmony_ci			writel(data, sfb->regs + SHADOWCON);
115162306a36Sopenharmony_ci		}
115262306a36Sopenharmony_ci		unregister_framebuffer(win->fbinfo);
115362306a36Sopenharmony_ci		if (win->fbinfo->cmap.len)
115462306a36Sopenharmony_ci			fb_dealloc_cmap(&win->fbinfo->cmap);
115562306a36Sopenharmony_ci		s3c_fb_free_memory(sfb, win);
115662306a36Sopenharmony_ci		framebuffer_release(win->fbinfo);
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci/**
116162306a36Sopenharmony_ci * s3c_fb_probe_win() - register an hardware window
116262306a36Sopenharmony_ci * @sfb: The base resources for the hardware
116362306a36Sopenharmony_ci * @win_no: The window number
116462306a36Sopenharmony_ci * @variant: The variant information for this window.
116562306a36Sopenharmony_ci * @res: Pointer to where to place the resultant window.
116662306a36Sopenharmony_ci *
116762306a36Sopenharmony_ci * Allocate and do the basic initialisation for one of the hardware's graphics
116862306a36Sopenharmony_ci * windows.
116962306a36Sopenharmony_ci */
117062306a36Sopenharmony_cistatic int s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
117162306a36Sopenharmony_ci			    struct s3c_fb_win_variant *variant,
117262306a36Sopenharmony_ci			    struct s3c_fb_win **res)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	struct fb_videomode initmode;
117562306a36Sopenharmony_ci	struct s3c_fb_pd_win *windata;
117662306a36Sopenharmony_ci	struct s3c_fb_win *win;
117762306a36Sopenharmony_ci	struct fb_info *fbinfo;
117862306a36Sopenharmony_ci	int palette_size;
117962306a36Sopenharmony_ci	int ret;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	init_waitqueue_head(&sfb->vsync_info.wait);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	palette_size = variant->palette_sz * 4;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
118862306a36Sopenharmony_ci				   palette_size * sizeof(u32), sfb->dev);
118962306a36Sopenharmony_ci	if (!fbinfo)
119062306a36Sopenharmony_ci		return -ENOMEM;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	windata = sfb->pdata->win[win_no];
119362306a36Sopenharmony_ci	initmode = *sfb->pdata->vtiming;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	WARN_ON(windata->max_bpp == 0);
119662306a36Sopenharmony_ci	WARN_ON(windata->xres == 0);
119762306a36Sopenharmony_ci	WARN_ON(windata->yres == 0);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	win = fbinfo->par;
120062306a36Sopenharmony_ci	*res = win;
120162306a36Sopenharmony_ci	win->variant = *variant;
120262306a36Sopenharmony_ci	win->fbinfo = fbinfo;
120362306a36Sopenharmony_ci	win->parent = sfb;
120462306a36Sopenharmony_ci	win->windata = windata;
120562306a36Sopenharmony_ci	win->index = win_no;
120662306a36Sopenharmony_ci	win->palette_buffer = (u32 *)(win + 1);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	ret = s3c_fb_alloc_memory(sfb, win);
120962306a36Sopenharmony_ci	if (ret) {
121062306a36Sopenharmony_ci		dev_err(sfb->dev, "failed to allocate display memory\n");
121162306a36Sopenharmony_ci		return ret;
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/* setup the r/b/g positions for the window's palette */
121562306a36Sopenharmony_ci	if (win->variant.palette_16bpp) {
121662306a36Sopenharmony_ci		/* Set RGB 5:6:5 as default */
121762306a36Sopenharmony_ci		win->palette.r.offset = 11;
121862306a36Sopenharmony_ci		win->palette.r.length = 5;
121962306a36Sopenharmony_ci		win->palette.g.offset = 5;
122062306a36Sopenharmony_ci		win->palette.g.length = 6;
122162306a36Sopenharmony_ci		win->palette.b.offset = 0;
122262306a36Sopenharmony_ci		win->palette.b.length = 5;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	} else {
122562306a36Sopenharmony_ci		/* Set 8bpp or 8bpp and 1bit alpha */
122662306a36Sopenharmony_ci		win->palette.r.offset = 16;
122762306a36Sopenharmony_ci		win->palette.r.length = 8;
122862306a36Sopenharmony_ci		win->palette.g.offset = 8;
122962306a36Sopenharmony_ci		win->palette.g.length = 8;
123062306a36Sopenharmony_ci		win->palette.b.offset = 0;
123162306a36Sopenharmony_ci		win->palette.b.length = 8;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	/* setup the initial video mode from the window */
123562306a36Sopenharmony_ci	initmode.xres = windata->xres;
123662306a36Sopenharmony_ci	initmode.yres = windata->yres;
123762306a36Sopenharmony_ci	fb_videomode_to_var(&fbinfo->var, &initmode);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	fbinfo->fix.type	= FB_TYPE_PACKED_PIXELS;
124062306a36Sopenharmony_ci	fbinfo->fix.accel	= FB_ACCEL_NONE;
124162306a36Sopenharmony_ci	fbinfo->var.activate	= FB_ACTIVATE_NOW;
124262306a36Sopenharmony_ci	fbinfo->var.vmode	= FB_VMODE_NONINTERLACED;
124362306a36Sopenharmony_ci	fbinfo->var.bits_per_pixel = windata->default_bpp;
124462306a36Sopenharmony_ci	fbinfo->fbops		= &s3c_fb_ops;
124562306a36Sopenharmony_ci	fbinfo->pseudo_palette  = &win->pseudo_palette;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	/* prepare to actually start the framebuffer */
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	ret = s3c_fb_check_var(&fbinfo->var, fbinfo);
125062306a36Sopenharmony_ci	if (ret < 0) {
125162306a36Sopenharmony_ci		dev_err(sfb->dev, "check_var failed on initial video params\n");
125262306a36Sopenharmony_ci		return ret;
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* create initial colour map */
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
125862306a36Sopenharmony_ci	if (ret == 0)
125962306a36Sopenharmony_ci		fb_set_cmap(&fbinfo->cmap, fbinfo);
126062306a36Sopenharmony_ci	else
126162306a36Sopenharmony_ci		dev_err(sfb->dev, "failed to allocate fb cmap\n");
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	s3c_fb_set_par(fbinfo);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	dev_dbg(sfb->dev, "about to register framebuffer\n");
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	/* run the check_var and set_par on our configuration. */
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	ret = register_framebuffer(fbinfo);
127062306a36Sopenharmony_ci	if (ret < 0) {
127162306a36Sopenharmony_ci		dev_err(sfb->dev, "failed to register framebuffer\n");
127262306a36Sopenharmony_ci		return ret;
127362306a36Sopenharmony_ci	}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	return 0;
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci/**
128162306a36Sopenharmony_ci * s3c_fb_set_rgb_timing() - set video timing for rgb interface.
128262306a36Sopenharmony_ci * @sfb: The base resources for the hardware.
128362306a36Sopenharmony_ci *
128462306a36Sopenharmony_ci * Set horizontal and vertical lcd rgb interface timing.
128562306a36Sopenharmony_ci */
128662306a36Sopenharmony_cistatic void s3c_fb_set_rgb_timing(struct s3c_fb *sfb)
128762306a36Sopenharmony_ci{
128862306a36Sopenharmony_ci	struct fb_videomode *vmode = sfb->pdata->vtiming;
128962306a36Sopenharmony_ci	void __iomem *regs = sfb->regs;
129062306a36Sopenharmony_ci	int clkdiv;
129162306a36Sopenharmony_ci	u32 data;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	if (!vmode->pixclock)
129462306a36Sopenharmony_ci		s3c_fb_missing_pixclock(vmode);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	data = sfb->pdata->vidcon0;
129962306a36Sopenharmony_ci	data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (clkdiv > 1)
130262306a36Sopenharmony_ci		data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
130362306a36Sopenharmony_ci	else
130462306a36Sopenharmony_ci		data &= ~VIDCON0_CLKDIR;	/* 1:1 clock */
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (sfb->variant.is_2443)
130762306a36Sopenharmony_ci		data |= (1 << 5);
130862306a36Sopenharmony_ci	writel(data, regs + VIDCON0);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	data = VIDTCON0_VBPD(vmode->upper_margin - 1) |
131162306a36Sopenharmony_ci	       VIDTCON0_VFPD(vmode->lower_margin - 1) |
131262306a36Sopenharmony_ci	       VIDTCON0_VSPW(vmode->vsync_len - 1);
131362306a36Sopenharmony_ci	writel(data, regs + sfb->variant.vidtcon);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	data = VIDTCON1_HBPD(vmode->left_margin - 1) |
131662306a36Sopenharmony_ci	       VIDTCON1_HFPD(vmode->right_margin - 1) |
131762306a36Sopenharmony_ci	       VIDTCON1_HSPW(vmode->hsync_len - 1);
131862306a36Sopenharmony_ci	writel(data, regs + sfb->variant.vidtcon + 4);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	data = VIDTCON2_LINEVAL(vmode->yres - 1) |
132162306a36Sopenharmony_ci	       VIDTCON2_HOZVAL(vmode->xres - 1) |
132262306a36Sopenharmony_ci	       VIDTCON2_LINEVAL_E(vmode->yres - 1) |
132362306a36Sopenharmony_ci	       VIDTCON2_HOZVAL_E(vmode->xres - 1);
132462306a36Sopenharmony_ci	writel(data, regs + sfb->variant.vidtcon + 8);
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci/**
132862306a36Sopenharmony_ci * s3c_fb_clear_win() - clear hardware window registers.
132962306a36Sopenharmony_ci * @sfb: The base resources for the hardware.
133062306a36Sopenharmony_ci * @win: The window to process.
133162306a36Sopenharmony_ci *
133262306a36Sopenharmony_ci * Reset the specific window registers to a known state.
133362306a36Sopenharmony_ci */
133462306a36Sopenharmony_cistatic void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	void __iomem *regs = sfb->regs;
133762306a36Sopenharmony_ci	u32 reg;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	writel(0, regs + sfb->variant.wincon + (win * 4));
134062306a36Sopenharmony_ci	writel(0, regs + VIDOSD_A(win, sfb->variant));
134162306a36Sopenharmony_ci	writel(0, regs + VIDOSD_B(win, sfb->variant));
134262306a36Sopenharmony_ci	writel(0, regs + VIDOSD_C(win, sfb->variant));
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (sfb->variant.has_shadowcon) {
134562306a36Sopenharmony_ci		reg = readl(sfb->regs + SHADOWCON);
134662306a36Sopenharmony_ci		reg &= ~(SHADOWCON_WINx_PROTECT(win) |
134762306a36Sopenharmony_ci			SHADOWCON_CHx_ENABLE(win) |
134862306a36Sopenharmony_ci			SHADOWCON_CHx_LOCAL_ENABLE(win));
134962306a36Sopenharmony_ci		writel(reg, sfb->regs + SHADOWCON);
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_cistatic int s3c_fb_probe(struct platform_device *pdev)
135462306a36Sopenharmony_ci{
135562306a36Sopenharmony_ci	const struct platform_device_id *platid;
135662306a36Sopenharmony_ci	struct s3c_fb_driverdata *fbdrv;
135762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
135862306a36Sopenharmony_ci	struct s3c_fb_platdata *pd;
135962306a36Sopenharmony_ci	struct s3c_fb *sfb;
136062306a36Sopenharmony_ci	int win;
136162306a36Sopenharmony_ci	int ret = 0;
136262306a36Sopenharmony_ci	u32 reg;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	platid = platform_get_device_id(pdev);
136562306a36Sopenharmony_ci	fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
136862306a36Sopenharmony_ci		dev_err(dev, "too many windows, cannot attach\n");
136962306a36Sopenharmony_ci		return -EINVAL;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	pd = dev_get_platdata(&pdev->dev);
137362306a36Sopenharmony_ci	if (!pd) {
137462306a36Sopenharmony_ci		dev_err(dev, "no platform data specified\n");
137562306a36Sopenharmony_ci		return -EINVAL;
137662306a36Sopenharmony_ci	}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	sfb = devm_kzalloc(dev, sizeof(*sfb), GFP_KERNEL);
137962306a36Sopenharmony_ci	if (!sfb)
138062306a36Sopenharmony_ci		return -ENOMEM;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	sfb->dev = dev;
138562306a36Sopenharmony_ci	sfb->pdata = pd;
138662306a36Sopenharmony_ci	sfb->variant = fbdrv->variant;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	spin_lock_init(&sfb->slock);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	sfb->bus_clk = devm_clk_get(dev, "lcd");
139162306a36Sopenharmony_ci	if (IS_ERR(sfb->bus_clk))
139262306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(sfb->bus_clk),
139362306a36Sopenharmony_ci				     "failed to get bus clock\n");
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	clk_prepare_enable(sfb->bus_clk);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (!sfb->variant.has_clksel) {
139862306a36Sopenharmony_ci		sfb->lcd_clk = devm_clk_get(dev, "sclk_fimd");
139962306a36Sopenharmony_ci		if (IS_ERR(sfb->lcd_clk)) {
140062306a36Sopenharmony_ci			ret = dev_err_probe(dev, PTR_ERR(sfb->lcd_clk),
140162306a36Sopenharmony_ci					    "failed to get lcd clock\n");
140262306a36Sopenharmony_ci			goto err_bus_clk;
140362306a36Sopenharmony_ci		}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci		clk_prepare_enable(sfb->lcd_clk);
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	pm_runtime_enable(sfb->dev);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	sfb->regs = devm_platform_ioremap_resource(pdev, 0);
141162306a36Sopenharmony_ci	if (IS_ERR(sfb->regs)) {
141262306a36Sopenharmony_ci		ret = PTR_ERR(sfb->regs);
141362306a36Sopenharmony_ci		goto err_lcd_clk;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	sfb->irq_no = platform_get_irq(pdev, 0);
141762306a36Sopenharmony_ci	if (sfb->irq_no < 0) {
141862306a36Sopenharmony_ci		ret = -ENOENT;
141962306a36Sopenharmony_ci		goto err_lcd_clk;
142062306a36Sopenharmony_ci	}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq,
142362306a36Sopenharmony_ci			  0, "s3c_fb", sfb);
142462306a36Sopenharmony_ci	if (ret) {
142562306a36Sopenharmony_ci		dev_err(dev, "irq request failed\n");
142662306a36Sopenharmony_ci		goto err_lcd_clk;
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	platform_set_drvdata(pdev, sfb);
143262306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	/* setup gpio and output polarity controls */
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	pd->setup_gpio();
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	writel(pd->vidcon1, sfb->regs + VIDCON1);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	/* set video clock running at under-run */
144162306a36Sopenharmony_ci	if (sfb->variant.has_fixvclk) {
144262306a36Sopenharmony_ci		reg = readl(sfb->regs + VIDCON1);
144362306a36Sopenharmony_ci		reg &= ~VIDCON1_VCLK_MASK;
144462306a36Sopenharmony_ci		reg |= VIDCON1_VCLK_RUN;
144562306a36Sopenharmony_ci		writel(reg, sfb->regs + VIDCON1);
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	/* zero all windows before we do anything */
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	for (win = 0; win < fbdrv->variant.nr_windows; win++)
145162306a36Sopenharmony_ci		s3c_fb_clear_win(sfb, win);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/* initialise colour key controls */
145462306a36Sopenharmony_ci	for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
145562306a36Sopenharmony_ci		void __iomem *regs = sfb->regs + sfb->variant.keycon;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci		regs += (win * 8);
145862306a36Sopenharmony_ci		writel(0xffffff, regs + WKEYCON0);
145962306a36Sopenharmony_ci		writel(0xffffff, regs + WKEYCON1);
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	s3c_fb_set_rgb_timing(sfb);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	/* we have the register setup, start allocating framebuffers */
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	for (win = 0; win < fbdrv->variant.nr_windows; win++) {
146762306a36Sopenharmony_ci		if (!pd->win[win])
146862306a36Sopenharmony_ci			continue;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci		ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
147162306a36Sopenharmony_ci				       &sfb->windows[win]);
147262306a36Sopenharmony_ci		if (ret < 0) {
147362306a36Sopenharmony_ci			dev_err(dev, "failed to create window %d\n", win);
147462306a36Sopenharmony_ci			for (; win >= 0; win--)
147562306a36Sopenharmony_ci				s3c_fb_release_win(sfb, sfb->windows[win]);
147662306a36Sopenharmony_ci			goto err_pm_runtime;
147762306a36Sopenharmony_ci		}
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	platform_set_drvdata(pdev, sfb);
148162306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	return 0;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cierr_pm_runtime:
148662306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cierr_lcd_clk:
148962306a36Sopenharmony_ci	pm_runtime_disable(sfb->dev);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	if (!sfb->variant.has_clksel)
149262306a36Sopenharmony_ci		clk_disable_unprepare(sfb->lcd_clk);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_cierr_bus_clk:
149562306a36Sopenharmony_ci	clk_disable_unprepare(sfb->bus_clk);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	return ret;
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci/**
150162306a36Sopenharmony_ci * s3c_fb_remove() - Cleanup on module finalisation
150262306a36Sopenharmony_ci * @pdev: The platform device we are bound to.
150362306a36Sopenharmony_ci *
150462306a36Sopenharmony_ci * Shutdown and then release all the resources that the driver allocated
150562306a36Sopenharmony_ci * on initialisation.
150662306a36Sopenharmony_ci */
150762306a36Sopenharmony_cistatic void s3c_fb_remove(struct platform_device *pdev)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	struct s3c_fb *sfb = platform_get_drvdata(pdev);
151062306a36Sopenharmony_ci	int win;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	for (win = 0; win < S3C_FB_MAX_WIN; win++)
151562306a36Sopenharmony_ci		if (sfb->windows[win])
151662306a36Sopenharmony_ci			s3c_fb_release_win(sfb, sfb->windows[win]);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if (!sfb->variant.has_clksel)
151962306a36Sopenharmony_ci		clk_disable_unprepare(sfb->lcd_clk);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	clk_disable_unprepare(sfb->bus_clk);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
152462306a36Sopenharmony_ci	pm_runtime_disable(sfb->dev);
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
152862306a36Sopenharmony_cistatic int s3c_fb_suspend(struct device *dev)
152962306a36Sopenharmony_ci{
153062306a36Sopenharmony_ci	struct s3c_fb *sfb = dev_get_drvdata(dev);
153162306a36Sopenharmony_ci	struct s3c_fb_win *win;
153262306a36Sopenharmony_ci	int win_no;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) {
153762306a36Sopenharmony_ci		win = sfb->windows[win_no];
153862306a36Sopenharmony_ci		if (!win)
153962306a36Sopenharmony_ci			continue;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci		/* use the blank function to push into power-down */
154262306a36Sopenharmony_ci		s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo);
154362306a36Sopenharmony_ci	}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	if (!sfb->variant.has_clksel)
154662306a36Sopenharmony_ci		clk_disable_unprepare(sfb->lcd_clk);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	clk_disable_unprepare(sfb->bus_clk);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	return 0;
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cistatic int s3c_fb_resume(struct device *dev)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	struct s3c_fb *sfb = dev_get_drvdata(dev);
155862306a36Sopenharmony_ci	struct s3c_fb_platdata *pd = sfb->pdata;
155962306a36Sopenharmony_ci	struct s3c_fb_win *win;
156062306a36Sopenharmony_ci	int win_no;
156162306a36Sopenharmony_ci	u32 reg;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	pm_runtime_get_sync(sfb->dev);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	clk_prepare_enable(sfb->bus_clk);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	if (!sfb->variant.has_clksel)
156862306a36Sopenharmony_ci		clk_prepare_enable(sfb->lcd_clk);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	/* setup gpio and output polarity controls */
157162306a36Sopenharmony_ci	pd->setup_gpio();
157262306a36Sopenharmony_ci	writel(pd->vidcon1, sfb->regs + VIDCON1);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	/* set video clock running at under-run */
157562306a36Sopenharmony_ci	if (sfb->variant.has_fixvclk) {
157662306a36Sopenharmony_ci		reg = readl(sfb->regs + VIDCON1);
157762306a36Sopenharmony_ci		reg &= ~VIDCON1_VCLK_MASK;
157862306a36Sopenharmony_ci		reg |= VIDCON1_VCLK_RUN;
157962306a36Sopenharmony_ci		writel(reg, sfb->regs + VIDCON1);
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	/* zero all windows before we do anything */
158362306a36Sopenharmony_ci	for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
158462306a36Sopenharmony_ci		s3c_fb_clear_win(sfb, win_no);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
158762306a36Sopenharmony_ci		void __iomem *regs = sfb->regs + sfb->variant.keycon;
158862306a36Sopenharmony_ci		win = sfb->windows[win_no];
158962306a36Sopenharmony_ci		if (!win)
159062306a36Sopenharmony_ci			continue;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci		shadow_protect_win(win, 1);
159362306a36Sopenharmony_ci		regs += (win_no * 8);
159462306a36Sopenharmony_ci		writel(0xffffff, regs + WKEYCON0);
159562306a36Sopenharmony_ci		writel(0xffffff, regs + WKEYCON1);
159662306a36Sopenharmony_ci		shadow_protect_win(win, 0);
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	s3c_fb_set_rgb_timing(sfb);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	/* restore framebuffers */
160262306a36Sopenharmony_ci	for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
160362306a36Sopenharmony_ci		win = sfb->windows[win_no];
160462306a36Sopenharmony_ci		if (!win)
160562306a36Sopenharmony_ci			continue;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		dev_dbg(dev, "resuming window %d\n", win_no);
160862306a36Sopenharmony_ci		s3c_fb_set_par(win->fbinfo);
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	pm_runtime_put_sync(sfb->dev);
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	return 0;
161462306a36Sopenharmony_ci}
161562306a36Sopenharmony_ci#endif
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci#ifdef CONFIG_PM
161862306a36Sopenharmony_cistatic int s3c_fb_runtime_suspend(struct device *dev)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct s3c_fb *sfb = dev_get_drvdata(dev);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	if (!sfb->variant.has_clksel)
162362306a36Sopenharmony_ci		clk_disable_unprepare(sfb->lcd_clk);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	clk_disable_unprepare(sfb->bus_clk);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	return 0;
162862306a36Sopenharmony_ci}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_cistatic int s3c_fb_runtime_resume(struct device *dev)
163162306a36Sopenharmony_ci{
163262306a36Sopenharmony_ci	struct s3c_fb *sfb = dev_get_drvdata(dev);
163362306a36Sopenharmony_ci	struct s3c_fb_platdata *pd = sfb->pdata;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	clk_prepare_enable(sfb->bus_clk);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	if (!sfb->variant.has_clksel)
163862306a36Sopenharmony_ci		clk_prepare_enable(sfb->lcd_clk);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	/* setup gpio and output polarity controls */
164162306a36Sopenharmony_ci	pd->setup_gpio();
164262306a36Sopenharmony_ci	writel(pd->vidcon1, sfb->regs + VIDCON1);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	return 0;
164562306a36Sopenharmony_ci}
164662306a36Sopenharmony_ci#endif
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
164962306a36Sopenharmony_ci#define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_cistatic struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = {
165262306a36Sopenharmony_ci	[0] = {
165362306a36Sopenharmony_ci		.has_osd_c	= 1,
165462306a36Sopenharmony_ci		.osd_size_off	= 0x8,
165562306a36Sopenharmony_ci		.palette_sz	= 256,
165662306a36Sopenharmony_ci		.valid_bpp	= (VALID_BPP1248 | VALID_BPP(16) |
165762306a36Sopenharmony_ci				   VALID_BPP(18) | VALID_BPP(24)),
165862306a36Sopenharmony_ci	},
165962306a36Sopenharmony_ci	[1] = {
166062306a36Sopenharmony_ci		.has_osd_c	= 1,
166162306a36Sopenharmony_ci		.has_osd_d	= 1,
166262306a36Sopenharmony_ci		.osd_size_off	= 0xc,
166362306a36Sopenharmony_ci		.has_osd_alpha	= 1,
166462306a36Sopenharmony_ci		.palette_sz	= 256,
166562306a36Sopenharmony_ci		.valid_bpp	= (VALID_BPP1248 | VALID_BPP(16) |
166662306a36Sopenharmony_ci				   VALID_BPP(18) | VALID_BPP(19) |
166762306a36Sopenharmony_ci				   VALID_BPP(24) | VALID_BPP(25) |
166862306a36Sopenharmony_ci				   VALID_BPP(28)),
166962306a36Sopenharmony_ci	},
167062306a36Sopenharmony_ci	[2] = {
167162306a36Sopenharmony_ci		.has_osd_c	= 1,
167262306a36Sopenharmony_ci		.has_osd_d	= 1,
167362306a36Sopenharmony_ci		.osd_size_off	= 0xc,
167462306a36Sopenharmony_ci		.has_osd_alpha	= 1,
167562306a36Sopenharmony_ci		.palette_sz	= 16,
167662306a36Sopenharmony_ci		.palette_16bpp	= 1,
167762306a36Sopenharmony_ci		.valid_bpp	= (VALID_BPP1248 | VALID_BPP(16) |
167862306a36Sopenharmony_ci				   VALID_BPP(18) | VALID_BPP(19) |
167962306a36Sopenharmony_ci				   VALID_BPP(24) | VALID_BPP(25) |
168062306a36Sopenharmony_ci				   VALID_BPP(28)),
168162306a36Sopenharmony_ci	},
168262306a36Sopenharmony_ci	[3] = {
168362306a36Sopenharmony_ci		.has_osd_c	= 1,
168462306a36Sopenharmony_ci		.has_osd_alpha	= 1,
168562306a36Sopenharmony_ci		.palette_sz	= 16,
168662306a36Sopenharmony_ci		.palette_16bpp	= 1,
168762306a36Sopenharmony_ci		.valid_bpp	= (VALID_BPP124  | VALID_BPP(16) |
168862306a36Sopenharmony_ci				   VALID_BPP(18) | VALID_BPP(19) |
168962306a36Sopenharmony_ci				   VALID_BPP(24) | VALID_BPP(25) |
169062306a36Sopenharmony_ci				   VALID_BPP(28)),
169162306a36Sopenharmony_ci	},
169262306a36Sopenharmony_ci	[4] = {
169362306a36Sopenharmony_ci		.has_osd_c	= 1,
169462306a36Sopenharmony_ci		.has_osd_alpha	= 1,
169562306a36Sopenharmony_ci		.palette_sz	= 4,
169662306a36Sopenharmony_ci		.palette_16bpp	= 1,
169762306a36Sopenharmony_ci		.valid_bpp	= (VALID_BPP(1) | VALID_BPP(2) |
169862306a36Sopenharmony_ci				   VALID_BPP(16) | VALID_BPP(18) |
169962306a36Sopenharmony_ci				   VALID_BPP(19) | VALID_BPP(24) |
170062306a36Sopenharmony_ci				   VALID_BPP(25) | VALID_BPP(28)),
170162306a36Sopenharmony_ci	},
170262306a36Sopenharmony_ci};
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_cistatic struct s3c_fb_driverdata s3c_fb_data_64xx = {
170562306a36Sopenharmony_ci	.variant = {
170662306a36Sopenharmony_ci		.nr_windows	= 5,
170762306a36Sopenharmony_ci		.vidtcon	= VIDTCON0,
170862306a36Sopenharmony_ci		.wincon		= WINCON(0),
170962306a36Sopenharmony_ci		.winmap		= WINxMAP(0),
171062306a36Sopenharmony_ci		.keycon		= WKEYCON,
171162306a36Sopenharmony_ci		.osd		= VIDOSD_BASE,
171262306a36Sopenharmony_ci		.osd_stride	= 16,
171362306a36Sopenharmony_ci		.buf_start	= VIDW_BUF_START(0),
171462306a36Sopenharmony_ci		.buf_size	= VIDW_BUF_SIZE(0),
171562306a36Sopenharmony_ci		.buf_end	= VIDW_BUF_END(0),
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		.palette = {
171862306a36Sopenharmony_ci			[0] = 0x400,
171962306a36Sopenharmony_ci			[1] = 0x800,
172062306a36Sopenharmony_ci			[2] = 0x300,
172162306a36Sopenharmony_ci			[3] = 0x320,
172262306a36Sopenharmony_ci			[4] = 0x340,
172362306a36Sopenharmony_ci		},
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci		.has_prtcon	= 1,
172662306a36Sopenharmony_ci		.has_clksel	= 1,
172762306a36Sopenharmony_ci	},
172862306a36Sopenharmony_ci	.win[0]	= &s3c_fb_data_64xx_wins[0],
172962306a36Sopenharmony_ci	.win[1]	= &s3c_fb_data_64xx_wins[1],
173062306a36Sopenharmony_ci	.win[2]	= &s3c_fb_data_64xx_wins[2],
173162306a36Sopenharmony_ci	.win[3]	= &s3c_fb_data_64xx_wins[3],
173262306a36Sopenharmony_ci	.win[4]	= &s3c_fb_data_64xx_wins[4],
173362306a36Sopenharmony_ci};
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci/* S3C2443/S3C2416 style hardware */
173662306a36Sopenharmony_cistatic struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
173762306a36Sopenharmony_ci	.variant = {
173862306a36Sopenharmony_ci		.nr_windows	= 2,
173962306a36Sopenharmony_ci		.is_2443	= 1,
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci		.vidtcon	= 0x08,
174262306a36Sopenharmony_ci		.wincon		= 0x14,
174362306a36Sopenharmony_ci		.winmap		= 0xd0,
174462306a36Sopenharmony_ci		.keycon		= 0xb0,
174562306a36Sopenharmony_ci		.osd		= 0x28,
174662306a36Sopenharmony_ci		.osd_stride	= 12,
174762306a36Sopenharmony_ci		.buf_start	= 0x64,
174862306a36Sopenharmony_ci		.buf_size	= 0x94,
174962306a36Sopenharmony_ci		.buf_end	= 0x7c,
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci		.palette = {
175262306a36Sopenharmony_ci			[0] = 0x400,
175362306a36Sopenharmony_ci			[1] = 0x800,
175462306a36Sopenharmony_ci		},
175562306a36Sopenharmony_ci		.has_clksel	= 1,
175662306a36Sopenharmony_ci	},
175762306a36Sopenharmony_ci	.win[0] = &(struct s3c_fb_win_variant) {
175862306a36Sopenharmony_ci		.palette_sz	= 256,
175962306a36Sopenharmony_ci		.valid_bpp	= VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
176062306a36Sopenharmony_ci	},
176162306a36Sopenharmony_ci	.win[1] = &(struct s3c_fb_win_variant) {
176262306a36Sopenharmony_ci		.has_osd_c	= 1,
176362306a36Sopenharmony_ci		.has_osd_alpha	= 1,
176462306a36Sopenharmony_ci		.palette_sz	= 256,
176562306a36Sopenharmony_ci		.valid_bpp	= (VALID_BPP1248 | VALID_BPP(16) |
176662306a36Sopenharmony_ci				   VALID_BPP(18) | VALID_BPP(19) |
176762306a36Sopenharmony_ci				   VALID_BPP(24) | VALID_BPP(25) |
176862306a36Sopenharmony_ci				   VALID_BPP(28)),
176962306a36Sopenharmony_ci	},
177062306a36Sopenharmony_ci};
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_cistatic const struct platform_device_id s3c_fb_driver_ids[] = {
177362306a36Sopenharmony_ci	{
177462306a36Sopenharmony_ci		.name		= "s3c-fb",
177562306a36Sopenharmony_ci		.driver_data	= (unsigned long)&s3c_fb_data_64xx,
177662306a36Sopenharmony_ci	}, {
177762306a36Sopenharmony_ci		.name		= "s3c2443-fb",
177862306a36Sopenharmony_ci		.driver_data	= (unsigned long)&s3c_fb_data_s3c2443,
177962306a36Sopenharmony_ci	},
178062306a36Sopenharmony_ci	{},
178162306a36Sopenharmony_ci};
178262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_cistatic const struct dev_pm_ops s3cfb_pm_ops = {
178562306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(s3c_fb_suspend, s3c_fb_resume)
178662306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(s3c_fb_runtime_suspend, s3c_fb_runtime_resume,
178762306a36Sopenharmony_ci			   NULL)
178862306a36Sopenharmony_ci};
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_cistatic struct platform_driver s3c_fb_driver = {
179162306a36Sopenharmony_ci	.probe		= s3c_fb_probe,
179262306a36Sopenharmony_ci	.remove_new	= s3c_fb_remove,
179362306a36Sopenharmony_ci	.id_table	= s3c_fb_driver_ids,
179462306a36Sopenharmony_ci	.driver		= {
179562306a36Sopenharmony_ci		.name	= "s3c-fb",
179662306a36Sopenharmony_ci		.pm	= &s3cfb_pm_ops,
179762306a36Sopenharmony_ci	},
179862306a36Sopenharmony_ci};
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cimodule_platform_driver(s3c_fb_driver);
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
180362306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver");
180462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1805