1802 lines · 45.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* linux/drivers/video/s3c-fb.c * * Copyright 2008 Openmoko Inc. * Copyright 2008-2010 Simtec Electronics *      Ben Dooks <ben@simtec.co.uk> *      http://armlinux.simtec.co.uk/ * * Samsung SoC Framebuffer driver*/ #include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/clk.h>#include <linux/fb.h>#include <linux/io.h>#include <linux/uaccess.h>#include <linux/interrupt.h>#include <linux/pm_runtime.h>#include <linux/platform_data/video_s3c.h> #include <video/samsung_fimd.h> /* This driver will export a number of framebuffer interfaces depending * on the configuration passed in via the platform data. Each fb instance * maps to a hardware window. Currently there is no support for runtime * setting of the alpha-blending functions that each window has, so only * window 0 is actually useful. * * Window 0 is treated specially, it is used for the basis of the LCD * output timings and as the control for the output power-down state.*/ /* note, the previous use of <mach/regs-fb.h> to get platform specific data * has been replaced by using the platform device name to pick the correct * configuration data for the system.*/ #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE#undef writel#define writel(v, r) do { \	pr_debug("%s: %08x => %p\n", __func__, (unsigned int)v, r); \	__raw_writel(v, r); \} while (0)#endif /* FB_S3C_DEBUG_REGWRITE */ /* irq_flags bits */#define S3C_FB_VSYNC_IRQ_EN	0 #define VSYNC_TIMEOUT_MSEC 50 struct s3c_fb; #define VALID_BPP(x) (1 << ((x) - 1)) #define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride))#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00)#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04)#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08)#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C) /** * struct s3c_fb_variant - fb variant information * @is_2443: Set if S3C2443/S3C2416 style hardware. * @nr_windows: The number of windows. * @vidtcon: The base for the VIDTCONx registers * @wincon: The base for the WINxCON registers. * @winmap: The base for the WINxMAP registers. * @keycon: The abse for the WxKEYCON registers. * @buf_start: Offset of buffer start registers. * @buf_size: Offset of buffer size registers. * @buf_end: Offset of buffer end registers. * @osd: The base for the OSD registers. * @osd_stride: stride of osd * @palette: Address of palette memory, or 0 if none. * @has_prtcon: Set if has PRTCON register. * @has_shadowcon: Set if has SHADOWCON register. * @has_blendcon: Set if has BLENDCON register. * @has_clksel: Set if VIDCON0 register has CLKSEL bit. * @has_fixvclk: Set if VIDCON1 register has FIXVCLK bits. */struct s3c_fb_variant {	unsigned int	is_2443:1;	unsigned short	nr_windows;	unsigned int	vidtcon;	unsigned short	wincon;	unsigned short	winmap;	unsigned short	keycon;	unsigned short	buf_start;	unsigned short	buf_end;	unsigned short	buf_size;	unsigned short	osd;	unsigned short	osd_stride;	unsigned short	palette[S3C_FB_MAX_WIN]; 	unsigned int	has_prtcon:1;	unsigned int	has_shadowcon:1;	unsigned int	has_blendcon:1;	unsigned int	has_clksel:1;	unsigned int	has_fixvclk:1;}; /** * struct s3c_fb_win_variant * @has_osd_c: Set if has OSD C register. * @has_osd_d: Set if has OSD D register. * @has_osd_alpha: Set if can change alpha transparency for a window. * @palette_sz: Size of palette in entries. * @palette_16bpp: Set if palette is 16bits wide. * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate *                register is located at the given offset from OSD_BASE. * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel. * * valid_bpp bit x is set if (x+1)BPP is supported. */struct s3c_fb_win_variant {	unsigned int	has_osd_c:1;	unsigned int	has_osd_d:1;	unsigned int	has_osd_alpha:1;	unsigned int	palette_16bpp:1;	unsigned short	osd_size_off;	unsigned short	palette_sz;	u32		valid_bpp;}; /** * struct s3c_fb_driverdata - per-device type driver data for init time. * @variant: The variant information for this driver. * @win: The window information for each window. */struct s3c_fb_driverdata {	struct s3c_fb_variant	variant;	struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN];}; /** * struct s3c_fb_palette - palette information * @r: Red bitfield. * @g: Green bitfield. * @b: Blue bitfield. * @a: Alpha bitfield. */struct s3c_fb_palette {	struct fb_bitfield	r;	struct fb_bitfield	g;	struct fb_bitfield	b;	struct fb_bitfield	a;}; /** * struct s3c_fb_win - per window private data for each framebuffer. * @windata: The platform data supplied for the window configuration. * @parent: The hardware that this window is part of. * @fbinfo: Pointer pack to the framebuffer info for this window. * @variant: The variant information for this window. * @palette_buffer: Buffer/cache to hold palette entries. * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ * @index: The window number of this window. * @palette: The bitfields for changing r/g/b into a hardware palette entry. */struct s3c_fb_win {	struct s3c_fb_pd_win	*windata;	struct s3c_fb		*parent;	struct fb_info		*fbinfo;	struct s3c_fb_palette	 palette;	struct s3c_fb_win_variant variant; 	u32			*palette_buffer;	u32			 pseudo_palette[16];	unsigned int		 index;}; /** * struct s3c_fb_vsync - vsync information * @wait:	a queue for processes waiting for vsync * @count:	vsync interrupt count */struct s3c_fb_vsync {	wait_queue_head_t	wait;	unsigned int		count;}; /** * struct s3c_fb - overall hardware state of the hardware * @slock: The spinlock protection for this data structure. * @dev: The device that we bound to, for printing, etc. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. * @lcd_clk: The clk (sclk) feeding pixclk. * @regs: The mapped hardware registers. * @variant: Variant information for this hardware. * @enabled: A bitmask of enabled hardware windows. * @output_on: Flag if the physical output is enabled. * @pdata: The platform configuration data passed with the device. * @windows: The hardware windows that have been claimed. * @irq_no: IRQ line number * @irq_flags: irq flags * @vsync_info: VSYNC-related information (count, queues...) */struct s3c_fb {	spinlock_t		slock;	struct device		*dev;	struct clk		*bus_clk;	struct clk		*lcd_clk;	void __iomem		*regs;	struct s3c_fb_variant	 variant; 	unsigned char		 enabled;	bool			 output_on; 	struct s3c_fb_platdata	*pdata;	struct s3c_fb_win	*windows[S3C_FB_MAX_WIN]; 	int			 irq_no;	unsigned long		 irq_flags;	struct s3c_fb_vsync	 vsync_info;}; /** * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode. * @win: The device window. * @bpp: The bit depth. */static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp){	return win->variant.valid_bpp & VALID_BPP(bpp);} /** * s3c_fb_check_var() - framebuffer layer request to verify a given mode. * @var: The screen information to verify. * @info: The framebuffer device. * * Framebuffer layer call to verify the given information and allow us to * update various information depending on the hardware capabilities. */static int s3c_fb_check_var(struct fb_var_screeninfo *var,			    struct fb_info *info){	struct s3c_fb_win *win = info->par;	struct s3c_fb *sfb = win->parent; 	dev_dbg(sfb->dev, "checking parameters\n"); 	var->xres_virtual = max(var->xres_virtual, var->xres);	var->yres_virtual = max(var->yres_virtual, var->yres); 	if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {		dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",			win->index, var->bits_per_pixel);		return -EINVAL;	} 	/* always ensure these are zero, for drop through cases below */	var->transp.offset = 0;	var->transp.length = 0; 	switch (var->bits_per_pixel) {	case 1:	case 2:	case 4:	case 8:		if (sfb->variant.palette[win->index] != 0) {			/* non palletised, A:1,R:2,G:3,B:2 mode */			var->red.offset		= 5;			var->green.offset	= 2;			var->blue.offset	= 0;			var->red.length		= 2;			var->green.length	= 3;			var->blue.length	= 2;			var->transp.offset	= 7;			var->transp.length	= 1;		} else {			var->red.offset	= 0;			var->red.length	= var->bits_per_pixel;			var->green	= var->red;			var->blue	= var->red;		}		break; 	case 19:		/* 666 with one bit alpha/transparency */		var->transp.offset	= 18;		var->transp.length	= 1;		fallthrough;	case 18:		var->bits_per_pixel	= 32; 		/* 666 format */		var->red.offset		= 12;		var->green.offset	= 6;		var->blue.offset	= 0;		var->red.length		= 6;		var->green.length	= 6;		var->blue.length	= 6;		break; 	case 16:		/* 16 bpp, 565 format */		var->red.offset		= 11;		var->green.offset	= 5;		var->blue.offset	= 0;		var->red.length		= 5;		var->green.length	= 6;		var->blue.length	= 5;		break; 	case 32:	case 28:	case 25:		var->transp.length	= var->bits_per_pixel - 24;		var->transp.offset	= 24;		fallthrough;	case 24:		/* our 24bpp is unpacked, so 32bpp */		var->bits_per_pixel	= 32;		var->red.offset		= 16;		var->red.length		= 8;		var->green.offset	= 8;		var->green.length	= 8;		var->blue.offset	= 0;		var->blue.length	= 8;		break; 	default:		dev_err(sfb->dev, "invalid bpp\n");		return -EINVAL;	} 	dev_dbg(sfb->dev, "%s: verified parameters\n", __func__);	return 0;} /** * s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock. * @sfb: The hardware state. * @pixclk: The pixel clock wanted, in picoseconds. * * Given the specified pixel clock, work out the necessary divider to get * close to the output frequency. */static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk){	unsigned long clk;	unsigned long long tmp;	unsigned int result; 	if (sfb->variant.has_clksel)		clk = clk_get_rate(sfb->bus_clk);	else		clk = clk_get_rate(sfb->lcd_clk); 	tmp = (unsigned long long)clk;	tmp *= pixclk; 	do_div(tmp, 1000000000UL);	result = (unsigned int)tmp / 1000; 	dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n",		pixclk, clk, result, result ? clk / result : clk); 	return result;} /** * s3c_fb_align_word() - align pixel count to word boundary * @bpp: The number of bits per pixel * @pix: The value to be aligned. * * Align the given pixel count so that it will start on an 32bit word * boundary. */static int s3c_fb_align_word(unsigned int bpp, unsigned int pix){	int pix_per_word; 	if (bpp > 16)		return pix; 	pix_per_word = (8 * 32) / bpp;	return ALIGN(pix, pix_per_word);} /** * vidosd_set_size() - set OSD size for a window * * @win: the window to set OSD size for * @size: OSD size register value */static void vidosd_set_size(struct s3c_fb_win *win, u32 size){	struct s3c_fb *sfb = win->parent; 	/* OSD can be set up if osd_size_off != 0 for this window */	if (win->variant.osd_size_off)		writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)				+ win->variant.osd_size_off);} /** * vidosd_set_alpha() - set alpha transparency for a window * * @win: the window to set OSD size for * @alpha: alpha register value */static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha){	struct s3c_fb *sfb = win->parent; 	if (win->variant.has_osd_alpha)		writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant));} /** * shadow_protect_win() - disable updating values from shadow registers at vsync * * @win: window to protect registers for * @protect: 1 to protect (disable updates) */static void shadow_protect_win(struct s3c_fb_win *win, bool protect){	struct s3c_fb *sfb = win->parent;	u32 reg; 	if (protect) {		if (sfb->variant.has_prtcon) {			writel(PRTCON_PROTECT, sfb->regs + PRTCON);		} else if (sfb->variant.has_shadowcon) {			reg = readl(sfb->regs + SHADOWCON);			writel(reg | SHADOWCON_WINx_PROTECT(win->index),				sfb->regs + SHADOWCON);		}	} else {		if (sfb->variant.has_prtcon) {			writel(0, sfb->regs + PRTCON);		} else if (sfb->variant.has_shadowcon) {			reg = readl(sfb->regs + SHADOWCON);			writel(reg & ~SHADOWCON_WINx_PROTECT(win->index),				sfb->regs + SHADOWCON);		}	}} /** * s3c_fb_enable() - Set the state of the main LCD output * @sfb: The main framebuffer state. * @enable: The state to set. */static void s3c_fb_enable(struct s3c_fb *sfb, int enable){	u32 vidcon0 = readl(sfb->regs + VIDCON0); 	if (enable && !sfb->output_on)		pm_runtime_get_sync(sfb->dev); 	if (enable) {		vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;	} else {		/* see the note in the framebuffer datasheet about		 * why you cannot take both of these bits down at the		 * same time. */ 		if (vidcon0 & VIDCON0_ENVID) {			vidcon0 |= VIDCON0_ENVID;			vidcon0 &= ~VIDCON0_ENVID_F;		}	} 	writel(vidcon0, sfb->regs + VIDCON0); 	if (!enable && sfb->output_on)		pm_runtime_put_sync(sfb->dev); 	sfb->output_on = enable;} /** * s3c_fb_set_par() - framebuffer request to set new framebuffer state. * @info: The framebuffer to change. * * Framebuffer layer request to set a new mode for the specified framebuffer */static int s3c_fb_set_par(struct fb_info *info){	struct fb_var_screeninfo *var = &info->var;	struct s3c_fb_win *win = info->par;	struct s3c_fb *sfb = win->parent;	void __iomem *regs = sfb->regs;	void __iomem *buf;	int win_no = win->index;	u32 alpha = 0;	u32 data;	u32 pagewidth; 	dev_dbg(sfb->dev, "setting framebuffer parameters\n"); 	pm_runtime_get_sync(sfb->dev);

Lines 1–500 of 1802.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t