⑥tiny4412 Linux驱动开发之LCD(framebuffer)驱动程序

本文介绍了在tiny4412开发板上进行Linux LCD驱动开发的过程,重点是framebuffer的使用。由于缺少X710屏幕的datasheet,开发者通过查找已有代码获取配置参数。开发工作主要包括添加平台总线数据,配置LCD控制器和设置启动logo来验证驱动有效性。文中提到Linux的framebuffer简化了驱动开发,通过映射DMA内存来提高视频数据处理效率。
摘要由CSDN通过智能技术生成

友善之臂对这个的支持还是比较坑的,我买的开发板用的是X710屏,我嘞个去,没有X710的datasheet,网上也找不到,只能另辟蹊径了,幸好,友善提供的源代码里有X710的配置参数,然后也可以顺利地显示出自己定制化的开机logo.

在说驱动之前,我们先来看一下框架知识,和以往驱动不同的是,这里用到了framebuffer,如下图是用framebuffer和以往驱动的差异的框架图:

如上,开发LCD驱动一般的做法就是上面两种形式,当然,也可以通过块设备的方式进行开发,这里主要讲字符形式的开发,后边有机会的话,用块设备驱动的形式做做,然后再分享.

以往字符设备一般分为3层,驱动控制层,驱动核心层和驱动适配器层.而framebuffer则简化为了两层,以方便快速开发,和减少开发人员的工作量,常见的驱动操作数据的形式就是copy_from_user()和copy_to_user(),这样的形式,可以实现对用户层和驱动层之间的隔离和区分管控,但是特殊情况则特殊对待,LCD我们一般应用的场景都是播放连续的画面,这就是形成目前视频播放的原理,目前的视频就是由一张一张连续的画面连续的显示得到的,一般视频都是数据量比较大,经典驱动架构copy_from_user()则对此是低效的方式,因为要把数据转移两遍,第一遍是从用户空间拷贝到内核空间,第二遍是从内核空间拷贝到物理器件(这里是LCD控制器),这种方式对于小数据量是没问题的,但是对于视频这种大数据量时,则会增加内核的负载和CPU的负载,因为这种转移是依靠CPU来实现的,所以为了针对视频这种特殊情况,Linux调整了视频驱动的架构,直接把本该内核操作的内存地址透明给用户空间,使用户空间直接给相应的内存数据,同时为了减轻CPU搬运数据的负担,一般SOC都是有集成DMA外设的,比如我们使用这款exynos 4412就具备,所以,在这里对于视频数据,我们采用映射DMA内存的方式搬运数据,以腾出CPU去做别的事情,而这块被映射的DMA内存的视频缓冲区则称之为显存,我们这里使用的是映射到RAM的显存,属于集成显存,还有一种叫独立显存,比如独立显卡,嵌入式设备为了节约成本,一般不用独立显存,同时,也视频架构除了帧缓冲之外,还用一种控制台的形式的驱动,比如VGA控制台,这里则只介绍framebuffer帧缓冲.

我们回到实际开发中,我先来说一下,这块开发的分工:

framebuffer核心层---->Linux内核完成(fbmem.c)

LCD控制器层---------->芯片原厂完成(这里是三星提供, s3c-fb.c)

LCD屏物理参数-------->我们自己添加(tiny4412-lcds.c)

我们需要做的就是只有一点,就是添加平台总线数据,因为这个芯片的LCD控制器只有一个,而LCD屏则可以使多选的,比如我们可以使用5寸的也可以使用7寸的,这两者肯定是有一定的差异的,有差异,就可以采取数据总线的形式,这里就是,LCD驱动采用了数据总线,我们根据自己的屏幕的实际物理特性,把相关参数添加到已经构件的总台总线的驱动数据层即可.

tiny4412的平台自定义数据在mach-tiny4412.c里面,我们直接搜fb即可,然后会在smdk4x12_machine_init()里面发现如下代码:

#ifdef CONFIG_TOUCHSCREEN_FT5X0X
	struct s3cfb_lcd *lcd = tiny4412_get_lcd();
	ft5x0x_pdata.screen_max_x = lcd->width;
	ft5x0x_pdata.screen_max_y = lcd->height;
#endif

其中的tiny4412_get_lcd()就是获取屏幕的参数的,这个函数定义在tiny4412-lcds.c,可以从这个c文件中发现X710屏幕的参数信息:

static struct s3cfb_lcd wsvga_x710 = {
	.width = 1024,			// 水平像素
	.height = 600,			// 垂直像素
	.p_width = 154,			// 物理水平尺寸
	.p_height = 90,			// 物理垂直尺寸,16:9
	.bpp = 24,			// 像素位宽,RGB888
	.freq = 61,			// 帧率

	.timing = {
		.h_fp = 84,		// 水平前肩,水平无效时间
		.h_bp = 84,		// 水平折返时间
		.h_sw = 88,		// 水平稳定时间
		.v_fp = 10,		// 垂直无效时间
		.v_fpe = 1,		// 均匀场垂直前肩
		.v_bp = 10,		// 垂直折返时间
		.v_bpe = 1,		// 均匀场垂直后肩
		.v_sw = 20,		// 垂直稳定时间
	},
	.polarity = {
		.rise_vclk = 1,		// 上升沿数据有效
		.inv_hsync = 1,		// 水平极性反转
		.inv_vsync = 1,		// 垂直极性反转
		.inv_vden = 0,		// 数据使能
	},
};

实际开发中,我们的工作量就是上面这么多,就这几个参数,别的都是要么Linux内核提供,要么芯片原厂提供,大大减小LCD驱动开发的难度,上面这些参数,一般响应屏幕的datasheet上会写出来,但这是个X710的,我没找到,不过,还是可以透过这个配置信息知道这个屏幕的属性,首先屏幕的分辨率是1024*600,物理尺寸是154x90(mm)接近于16:9,像素位宽是24,也就是RGB三原色各占8位,帧率从上面的数据可以大概算一下,接近于61Hz,一般也都要求至少60Hz才对人眼不会造成眩晕.然后timing结构体里的数据都是要datasheet上提供的,但是下载不到这个datasheet,这里没什么好说的,我都有写注释,自己可以百度到详细答案.polarity结构体里的东西也给了注释,就不多说了.填完这个之后,我们来验证一下效果,需要做以下事情:

1).让内核加载适合我们屏幕的参数:

这一步很坑,因为我根本没找到在哪里去做配置,这个内核没有使用设备树,然后也没有向全志一样使用script.bin的形式配置,make menuconfig也没找到在哪里配置,然后看到顶层目录的.config文件里有一个uboot传参的句式:

CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootfstype=ext4 init=/linuxrc console=ttySAC0,115200 lcd=HD700 ctp=2 skipcali=y"

然后知道应该是可以通过uboot进行选择,但是,我这里直接了当,因为配置都是通过tiny4412_get_lcd()这个函数来获取的,所以,我直接修改这个函数即可,做如下修改:

struct s3cfb_lcd *tiny4412_get_lcd(void)
{
+++	lcd_idx = 7;
	return tiny4412_lcd_config[lcd_idx].lcd;
}

其中新增加一句lcd_idx = 7;是因为X710是可选配置结构体里的第7个,这样.我们就选中了我们的屏幕X710.

2),配置了LCD之后,我们来验证一下LCD是否有效可用

最简单的方式就是打开启动logo,如果开机有启动logo,就说明配置成功,如下,首先make menuconfig:

画红框的都要选中,然后,重新编译内核,把新生成的zImage下载到内存卡,从内存卡启动,然后,如果顺利的话,就可以看到4个小企鹅了,一个核心对应一个小企鹅,4个代表有4颗核心,8个则代表8核心,以此类推.

下面这张图是填参数的依据,因为这里没有找到X710的datasheet,所以,这里就不讲这些了,不过如上都是有注明什么对应什么的,一般看一下,就可以明白了.(从左到右,从上到下依次是SOC的LCD控制器时序图,LCD屏幕datasheet时序图,Linux内核定义的时序图,就这3个,第4个和第2个是一个,别看错了,它们虽然名字不同,但是却是描述的同一个物理特性)

除了这些之外,我们还来做一个自己定制化的启动logo,玩一下,需要如下步骤:

1, 网上随便下载一张图片,比如jpg格式的,然后,我们通过GIMP软件把图片
像素修改成1028x600大小,位色改为224色,然后保存为ppm格式的ASCii文件

2, 把ppm图片放在如下路径drivers/video/logo/logo_meizi_clut224.ppm

3, drivers/video/logo/Makefile
	obj-$(CONFIG_LOGO_MEIZI_CLUT224)	+= logo_meizi_clut224.o
	
4, drivers/video/logo/Kconfig
		config LOGO_MEIZI_CLUT224
		bool "Meizi Logo is very beautiful"
		help 
			meizi meizi ,sexy lady
	
5,将logo文件编译进内核:
	Device Drivers  --->
		Graphics support  --->
			[*] Bootup logo  ---> //提供启动的图片
				[*]   Standard black and white Linux logo (NEW)
				[*]   Standard 16-color Linux logo (NEW)
				[*]   Standard 224-color Linux logo (NEW) //默认小企鹅图片
				[*]   Meizi Logo is very beautiful
		
6, 指定使用哪个logo:
	include/linux/linux_logo.h
		extern const struct linux_logo logo_meizi_clut224;


	drivers/video/logo/logo.c
		|
		#ifdef CONFIG_LOGO_MEIZI_CLUT224
		            logo = &logo_meizi_clut224;
		#endif
7, 编译内核:
	make zImage 

下载zImage到内存卡,然后启动就OK了.(因为uboot没有移植好,没法通过tftp下载,烧进mmc又麻烦,有机会再搞一下uboot).

因为之前领导说让我"兼职"做LCD这一块,所以,就准备深入学习这一块,但是目前实际上,并没有拿到驱动部门的任何资源,处境很尴尬,也始终得不到岗位调度,所以现在还是靠自己了,本来计划自己实现一个LCD驱动.目前情况来看,还有更迫切的事,所以先贴一个三星官方的,有空再替换成自己写的.

#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 <mach/map.h>
#include <mach/sysmmu.h>
#include <mach/s3c-fb.h>
#include <plat/regs-fb-v4.h>
#include <plat/fb.h>

#ifdef CONFIG_ION_EXYNOS
#define CONFIG_FB_ION_EXYNOS
#endif

#if defined(CONFIG_FB_ION_EXYNOS)
#include <linux/dma-buf.h>
#include <plat/iovmm.h>
#include <linux/sw_sync.h>
#include <plat/devs.h>
#include <linux/ion.h>
#include <linux/kthread.h>
#endif

/* 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	60

#undef  CHECK_BANDWIDTH
#define MAX_BW_PER_WINDOW	(800 * 1280 * 4 * 60)

/* disable blank */
#define ENABLE_FB_BLANK		0

struct s3c_fb;
extern struct ion_device *exynos_ion_dev;

#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.
 * @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;
};

#if defined(CONFIG_FB_ION_EXYNOS)
struct s3c_dma_buf_data {
	struct ion_handle *ion_handle;
	struct dma_buf *dma_buf;
	struct dma_buf_attachment *attachment;
	struct sg_table *sg_table;
	dma_addr_t dma_addr;
	struct sync_fence *fence;
};

struct s3c_reg_data {
	struct list_head	list;
	u32			shadowcon;
	u32			wincon[S3C_FB_MAX_WIN];
	u32			win_rgborder[S3C_FB_MAX_WIN];
	u32			winmap[S3C_FB_MAX_WIN];
	u32			vidosd_a[S3C_FB_MAX_WIN];
	u32			vidosd_b[S3C_FB_MAX_WIN];
	u32			vidosd_c[S3C_FB_MAX_WIN];
	u32			vidosd_d[S3C_FB_MAX_WIN];
	u32			vidw_alpha0[S3C_FB_MAX_WIN];
	u32			vidw_alpha1[S3C_FB_MAX_WIN];
	u32			blendeq[S3C_FB_MAX_WIN - 1];
	u32			vidw_buf_start[S3C_FB_MAX_WIN];
	u32			vidw_buf_end[S3C_FB_MAX_WIN];
	u32			vidw_buf_size[S3C_FB_MAX_WIN];
	struct s3c_dma_buf_data	dma_buf_data[S3C_FB_MAX_WIN];
};
#endif

/**
 * 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.
 * @varint: 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;

#if defined(CONFIG_FB_ION_EXYNOS)
	struct s3c_dma_buf_data	dma_buf_data;
	struct fb_var_screeninfo prev_var;
	struct fb_fix_screeninfo prev_fix;

	int			fps;
#endif
};

/**
 * 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;

#if defined(CONFIG_FB_ION_EXYNOS)	
	ktime_t				timestamp;
	bool				active;
	int					irq_refcount;
	struct mutex		irq_lock;
	struct task_struct	*thread;
#endif
	unsigned int		count;
};

/**
 * struct s3c_fb_user_window - User window information
 * @x: X position of user window.
 * @y: Y position of user window.
 */
struct s3c_fb_user_window {
	int x;
	int y;
};

/**
 * struct s3c_fb_user_chroma - User chroma key information
 * @enabled: Enabled/Disabled chroma key.
 * @red:     red color key value for transparent pixel effect.
 * @green:   green color key value for transparent pixel effect.
 * @blue:    blue color key value for transparent pixel effect.
 */
struct s3c_fb_user_chroma {
	int				enabled;
	unsigned char	red;
	unsigned char	green;
	unsigned char	blue;
};

/**
 * struct s3c_fb - overall hardware state of the hardware
 * @slock: The spinlock protection for this data sturcture.
 * @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;

#if defined(CONFIG_FB_ION_EXYNOS) 
	struct mutex		output_lock;
	struct ion_client	*fb_ion_client;

	struct list_head	update_regs_list;
	struct mutex		update_regs_list_lock;
	struct kthread_worker	update_regs_worker;
	struct task_struct	*update_regs_thread;
	struct kthread_work	update_regs_work;

	struct sw_sync_timeline *timeline;
	int			timeline_max;
#endif
};

#if defined(CONFIG_FB_ION_EXYNOS)
static bool s3c_fb_validate_x_alignment(struct s3c_fb *sfb, int x, u32 w,
		u32 bits_per_pixel)
{
	uint8_t pixel_alignment = 32 / bits_per_pixel;

	if (x % pixel_alignment) {
		dev_err(sfb->dev, "left X coordinate not properly aligned to "
				"%u-pixel boundary (bpp = %u, x = %u)\n",
				pixel_alignment, bits_per_pixel, x);
		return 0;
	}
	if ((x + w) % pixel_alignment) {
		dev_err(sfb->dev, "right X coordinate not properly aligned to "
				"%u-pixel boundary (bpp = %u, x = %u, w = %u)\n",
				pixel_alignment, bits_per_pixel, x, w);
		return 0;
	}

	return 1;
}
#endif

/**
 * 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		= 4;
			var->green.offset	= 2;
			var->blue.offset	= 0;
			var->red.length		= 5;
			var->green.length	= 3;
			var->blue.length	= 2;
			var->transp.offset	= 7;
			var->transp.length	= 1;
		} else {
			var->red.offset	= 0;
			var->red.length	= var->bits_per_pixel;
			var->green	= var->red;
			var->blue	= var->red;
		}
		break;

	case 19:
		/* 666 with one bit alpha/transparency */
		var->transp.offset	= 18;
		var->transp.length	= 1;
	case 18:
		var->bits_per_pixel	= 32;

		/* 666 format */
		var->red.offset		= 12;
		var->green.offset	= 6;
		var->blue.offset	= 0;
		var->red.length		= 6;
		var->green.length	= 6;
		var->blue.length	= 6;
#ifdef CONFIG_FB_S3C_ORDER_BGR
		swap(var->red.offset, var->blue.offset);
#endif
		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;
#ifdef CONFIG_FB_S3C_ORDER_BGR
		swap(var->red.offset, var->blue.offset);
#endif
		break;

	case 32:
	case 28:
	case 25:
		var->transp.length	= var->bits_per_pixel - 24;
		var->transp.offset	= 24;
		/* drop through */
	case 24:
		/* our 24bpp is unpacked, so 32bpp */
		var->bits_per_pixel	= 32;
		var->red.offset		= 16;
		var->red.length		= 8;
		var->green.offset	= 8;
		var->green.length	= 8;
		var->blue.offset	= 0;
		var->blue.length	= 8;
#ifdef CONFIG_FB_S3C_ORDER_BGR
		swap(var->red.offset, var->blue.offset);
#endif
		break;

	default:
		dev_err(sfb->dev, "invalid bpp\n");
	}

#if defined(CONFIG_FB_ION_EXYNOS)
	win->fps = 60;
#endif

	dev_dbg(sfb->dev, "%s: verified parameters\n", __func__);
	return 0;
}

/**
 * s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock.
 * @sfb: The hardware state.
 * @pixclock: The pixel clock wanted, in picoseconds.
 *
 * Given the specified pixel clock, work out the necessary divider to get
 * close to the output frequency.
 */
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->va
  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值