利用linux驱动思想实现s5pv210的lcd控制器

本文章主要参考自韦东山老师的新一期裸板视屏中LCD显示章节


最近在看驱动,发现部分。韦老师的裸机部分已经使用了驱动的操作分层和数据分离的思想,回来再刷一遍,顺便做好记录。

1.LCD的扫描显示原理大家可以看下面这篇文章,讲的特别清晰,我就不重复。

http://www.cnblogs.com/shangdawei/p/4760933.html

2.S5PV210的LCD组成

利用linux驱动思想实现s5pv210的lcd控制器_第1张图片

因为S5PV210的LCD支持很多种显示功能,框图看起来比较复杂,所以我们只关注我们用到的。

我用红色框框起来了。

1.默认就使用DMA功能

2.RGB接口最大支持24根线(我的板子是RGB的TFT)



S5PV210支持多窗口显示。

因为处理器支持很多个虚拟窗口,而且每个窗口大小都可以配置,在使能多个窗口的情况下,可以实现一些复杂的图像显示。

比如下图使用三个窗口,一个是主窗口(黑框)和我们的屏幕一样。一个比较小(红框)。最后一个也比较小(紫框)。在配置好着三个串口的大小和地址后,再配置那个窗口在前,那个在后。(在前的内容可以覆盖在后的内容)

利用linux驱动思想实现s5pv210的lcd控制器_第2张图片

有这三个窗口叠加就可以实现,类似新闻联播中,字幕滚动,台标显示和主持人窗口,三个互补影响的叠加显示。

我们的配置不使用如此复杂,只使用一个窗口。其它多个的配置也是类似。


3.主要寄存器

VIDCON0

这个寄存器主要的几个寄存器我都画出来了,我们用RGB接口,时钟需要分频,使能

利用linux驱动思想实现s5pv210的lcd控制器_第3张图片

利用linux驱动思想实现s5pv210的lcd控制器_第4张图片


利用linux驱动思想实现s5pv210的lcd控制器_第5张图片

利用linux驱动思想实现s5pv210的lcd控制器_第6张图片


VIDCON1

这个主要是一些引脚极性,看要不要

利用linux驱动思想实现s5pv210的lcd控制器_第7张图片

利用linux驱动思想实现s5pv210的lcd控制器_第8张图片


下面两个是时序相关的

利用linux驱动思想实现s5pv210的lcd控制器_第9张图片

利用linux驱动思想实现s5pv210的lcd控制器_第10张图片

屏幕分辨率配置

利用linux驱动思想实现s5pv210的lcd控制器_第11张图片


WINDOW0

利用linux驱动思想实现s5pv210的lcd控制器_第12张图片




SHODOWCON

利用linux驱动思想实现s5pv210的lcd控制器_第13张图片

窗口1左上角坐标

利用linux驱动思想实现s5pv210的lcd控制器_第14张图片

窗口1右下角坐标

利用linux驱动思想实现s5pv210的lcd控制器_第15张图片

窗口1的大小

利用linux驱动思想实现s5pv210的lcd控制器_第16张图片


窗口1显存的起始地址




窗口1显存的结束地址

利用linux驱动思想实现s5pv210的lcd控制器_第17张图片

显示要经过的路径

利用linux驱动思想实现s5pv210的lcd控制器_第18张图片


源码主要分两层。


一层是LCD层(类似platform_device)

另一层是LCD控制器层(类似platform_driver)


先看一下lcd层

抽象出来的一些lcd公有的参数

enum {
	NORMAL = 0,
	INVERT = 1,
};


/*
 * lcd引脚极性
 * NORMAL :正常极性
 * INVERT :翻转极性
 */
struct pins_polarity {
	int vclk;		/* NORMAL:在下降沿获取数据 */
	int vsync;		/* NORMAL:在上升沿获取数据*/
	int hsync;		/* NORMAL:在上升沿获取数据 */
	int rgb;
	int de;			/* NORMAL:在高电平有效 */
};


struct time_sequence {
	/* vclk */
	int vclk;

	/* 水平方向 */
	int thp;		/* Horizontal Pulse Width */	
	int thb;		/* Horizontal Back Porch */
	int thf;		/* Horizontal Front Porch */
	/* 垂直方向 */
	int tvp;		/* Vertical Pulse Width */
	int tvb;		/* Vertical Back Porch */	
	int tvf;		/* Vertical Front Porch */

};



/*
 * lcd 参数
 */
struct lcd_params {
	char *name;
	
	/* 引脚极性 */
	struct pins_polarity pins_pol;
	
	/* 时序 */
	struct time_sequence time_seq;
	
	/* 分辨率 bpp  */
	int xres;
	int yres;
	int bpp;
	
	/* framebuffer地址 */
	unsigned int fb_base;	

	/* 那个窗口 */
	int id;

	
};


为所有lcd抽象的公有的注册接口

#define LCD_NUM	10

static struct lcd_params *lcd_paramss[LCD_NUM];
static struct lcd_params *s_lcd_params;


int register_lcd(struct lcd_params *p_lcd_par)
{
	int i;

	for(i=0; iname,name))
		{
			s_lcd_params = lcd_paramss[i];
			return i;
		}
	}
	
	return -1;
}

struct lcd_params * get_lcd_params(void)
{
	return s_lcd_params;
}


void lcd_enable(void)
{
	lcd_controller_enable();
}

void lcd_disable(void)
{
	lcd_controller_disable();
}

下面是soc的lcd控制器层

struct lcd_controller {
	char *name;
	void (*init)(struct lcd_params *lcd_par);
	void (*enable)(void);
	void (*disable)(void);
};

#define LCD_CONTROLLER_NUM	10

static struct lcd_controller *lcd_controllers[LCD_CONTROLLER_NUM];
static struct lcd_controller *s_lcd_controller;


/*
 *	公有的注册lcd的控制器接口
 */
int register_lcd_controller(struct lcd_controller *p_lcd_ctl)
{
	int i;

	for(i=0; iname,name))
		{
			s_lcd_controller = lcd_controllers[i];
			return i;
		}
	}
	
	return -1;
}

/*
 * 公有的lcd on接口
 */
void lcd_controller_enable(void)
{
	if(s_lcd_controller)
	{
		s_lcd_controller->enable();
		
	}
}

/*
 * 公有的lcd off接口
 */
void lcd_controller_disable(void)
{
	if(s_lcd_controller)
	{
		s_lcd_controller->disable();
	}
}





/*
 *	向上:接收不同lcd的参数
 *  向下:使用这些参数设置对应的lcd控制器
 */

int lcd_controller_init(struct lcd_params *p_lcd_params)
{
	if(s_lcd_controller)
	{
		s_lcd_controller->init(p_lcd_params);
		return 0;
	}

	return -1;
}


/* 注册函数 */
int lcd_controller_add(void)
{
	s5pv210_lcd_controller_add();
	
}


下面就是和硬件相关测私有数据和配置。

数据

#define FB_BASE	0x40000000


static struct lcd_params lcd_7_0_params = {
	.name = "lcd_7.0",
	.pins_pol = {
		.vclk	= INVERT,		/* NORMAL:在下降沿获取数据 */
		.vsync	= INVERT,		/* NORMAL:在上升沿获取数据*/
		.hsync	= INVERT,		/* NORMAL:在上升沿获取数据 */
		.rgb	= NORMAL,
		.de		= NORMAL,		/* NORMAL:在高电平有效 */
	},
	.time_seq = {
		/* vclk */
		.vclk	= 52,	/* Mhz */

		/* 水平方向 */
		.thp	= 20,		/* Horizontal Pulse Width */	
		.thb	= 140,		/* Horizontal Back Porch */
		.thf	= 160,		/* Horizontal Front Porch */
		/* 垂直方向 */
		.tvp	= 3,		/* Vertical Pulse Width */
		.tvb	= 20,		/* Vertical Back Porch */	
		.tvf	= 12,		/* Vertical Front Porch */	
	},
	.xres = 1024,
	.yres = 600,
	.bpp  = 16,
	.fb_base = FB_BASE,
	.id = 0,
};



int lcd_7_0_add(void)
{
	return register_lcd(&lcd_7_0_params);
}

操作

#define HCLK_DSYS  166


/* lcd相关引脚初始化 */
static void x210_lcd_pins_init(void)
{
	/* lcd引脚 */
	__REG(GPF0CON) = 0x22222222;
	__REG(GPF1CON) = 0x22222222;
	__REG(GPF2CON) = 0x22222222;
	__REG(GPF3CON) = 0x00222222;

	/* 初始化背光引脚 */
	__REG(GPD0CON) &= ~(0x0f);
	__REG(GPD0CON) |= 0x01;

}



/* 根据传入的参数设置lcd控制寄存器 */
static void s5pv210_lcd_controller_init(struct lcd_params * p_lcd_params)
{
	int clkval_f;
	int bpp;

	x210_lcd_pins_init();


	__REG(S5PV210_DISPLAY_CONTROL) = 0x02;

	/*	[28:26]	:VIDOUT   RGB interface
	 *	[18]	:RGSPSEL  RGB parallel format 0
	 *  [17]	:PNRMODE  
	 *  [16] 	:CLKVALUP Selects CLKVAL_F update timing control 0 aways
	 *  [13:6]	:CLKVAL_F VCLK = HCLK / (CLKVAL+1), where CLKVAL >= 1
	 *  [5]		:VCLKFREE Controls VCLK Free Run  
	 *  [4]		:CLKDIR Selects the clock source
	 *  [2]		:CLKSEL_F Selects the video clock source.
	 *  [1]		:ENVID Enables/ disables video output and logic immediately.
	 *  [0]		:ENVID_F  Enables/ disables video output and logic at current frame end
	 */
	//clkval_f = (HCLK_DSYS / p_lcd_params->time_seq.vclk - 1) ? (HCLK_DSYS / p_lcd_params->time_seq.vclk - 1) : 1;
	clkval_f = 3;		/* 最快只能为3,否则不能显示 */
	__REG(S5PV210_VIDCON0) = (0<<26)|(0<<18)|(clkval_f<<6)|(1<<4)|(0<<2);

	__REG(S5PV210_VIDCON0) |= (1<<1)|(1<<0);


	/* 	[7]		:IVCLK  Controls the polarity of the VCLK active edge.
	 *	[6]		:IHSYNC  Specifies the HSYNC pulse polarity. 
	 *	[5]		:IVSYNC  Specifies the VSYNC pulse polarity.
	 *	[4]		:IVDEN  Specifies the VDEN signal polarity
	 */
	__REG(S5PV210_VIDCON1) = 	(p_lcd_params->pins_pol.vclk<<7)	|\
								(p_lcd_params->pins_pol.hsync<<6)	|\
								(p_lcd_params->pins_pol.vsync<<5)	|\
								(p_lcd_params->pins_pol.de<<4);

	/*	[27]	:RGB_SKIP_EN  Enables the RGB skip mode (only where RGBSPSEL == 1’b0)
	 *	[21:19]	:RGB_ORDER_E  Controls RGB interface output order
	 */		
	__REG(S5PV210_VIDCON2) = (0<<19);


	/*	[23:16]	:VBPD  Vertical back porch  VBPD = tvb - 1
	 *	[15:8]	:VFPD  Vertical front porch	VFPD = tvf - 1
	 *	[7:0]	:VSPW  Vertical sync pulse width VSPW = tvp - 1 
	 */
	__REG(S5PV210_VIDTCON0) = 	((p_lcd_params->time_seq.tvb-1)<<16) 	|\
								((p_lcd_params->time_seq.tvf-1)<<8) 	|\
								((p_lcd_params->time_seq.tvp-1)<<0);

	
	/*	[23:16]	:HBPD  Horizontal back porch  HBPD = thb - 1
	 *	[15:8]	:HFPD  Horizontal front porch	HFPD = thf - 1
	 *	[7:0]	:HSPW  Horizontal sync pulse width  HSPW = thp - 1 
	 */
	__REG(S5PV210_VIDTCON1) = 	((p_lcd_params->time_seq.thb-1)<<16) 	|\
								((p_lcd_params->time_seq.thf-1)<<8) 	|\
								((p_lcd_params->time_seq.thp-1)<<0);

	/*	物理屏幕分辨率
	 *	[21:11]	:LINEVAL  the vertical size of display.(LINEVAL + 1) should be even.
	 *	[10:0]	:HOZVAL  Determines the horizontal size of display.
	 */
	__REG(S5PV210_VIDTCON2) =	(p_lcd_params->yres<<11)|(p_lcd_params->xres<<0);


	
	/*	[15]	:WSWP_F  Specifies the Word swap control bit
	 *	[5:2]	:BPPMODE_F  Selects the Bits Per Pixel (BPP) mode for Window image.
	 *			0011 = 8 bpp ( palletized )
	 *			0101 = 16 bpp ( non-palletized, R:5-G:6-B:5 )
	 *			1011 = Unpacked 24 bpp ( non-palletized R:8-G:8-B:8 )
	 *	[0]		:ENWIN_F  Enables/ disables video output and logic immediately
	 */
	bpp =	p_lcd_params->bpp == 8 	? 0x3 : \
			p_lcd_params->bpp == 16 ? 0x5 : \
			0xb;	/* 24/32bpp */
	__REG(S5PV210_WINCON0) = (p_lcd_params->pins_pol.rgb<<15)|(bpp<<2)|(1<<0);


	/*	设置屏幕左上角坐标
	 *	[21:11]		:OSD_LeftTopX_F  Specifies the horizontal screen coordinate
	 *	[10:0]		:OSD_LeftTopY_F  Specifies the vertical screen coordinate
	 */
	__REG(S5PV210_VIDOSD0A) = (0<<11)|(0<<10);

	
	/*	设置屏幕右上角坐标
	 *	[21:11]	:OSD_RightBotX_F  Specifies the horizontal screen coordinate
	 *	[10:0]	:OSD_RightBotY_F  Specifies the vertical screen coordinate 
	 */
	__REG(S5PV210_VIDOSD0B) = ((p_lcd_params->xres-1)<<11)|((p_lcd_params->yres-1)<<0);

	
	/*	窗口0的尺寸
	 *	OSDSIZE [23:0] Specifies the Window Size 0
	 *	For example, Height * Width (Number of Word)
	 */
	__REG(S5PV210_VIDOSD0C) = p_lcd_params->xres * p_lcd_params->yres;

	
	/*	framebuffer的起始地址
	 *	[31:0]:VBASEU_F  Specifies A [31:0] of the start address for Video frame buffer.
	 */
	__REG(S5PV210_VIDW00ADD0B0) = p_lcd_params->fb_base;


	/*	framebuffer end of address
	 *	[31:0]:VBASEL_F   the end address for Video frame buffer. 
	 *   VBASEL = VBASEU +(PAGEWIDTH+OFFSIZE) x (LINEVAL+1)
	 */
	bpp = 	p_lcd_params->bpp == 8 	? 1 : \
			p_lcd_params->bpp == 16 ? 2 : \
			4;	/* 24/32bpp */
	__REG(S5PV210_VIDW00ADD0B1) = p_lcd_params->fb_base + p_lcd_params->xres * p_lcd_params->yres * bpp;
	 
	/* 
	 *	[0]	:C0_EN_F  Enables Channel 0
	 */
	__REG(S5PV210_SHADOWCON) |= (1<id);

	printf("s5pv210_lcd_controller_init\n\r");
	
}

static void s5pv210_lcd_controller_enable(void)
{
	/* on back right */
	__REG(GPD0DAT) &= ~0x01;

	printf("s5pv210_lcd_controller_enable\n\r");
}


static void s5pv210_lcd_controller_disable(void)
{
	/* off back right */
	__REG(GPD0DAT) |= 0x01;
	printf("s5pv210_lcd_controller_disable\n\r");
}

static struct lcd_controller s5pv210_lcd_controller = {
	.name		= "s5pv210",
	.init 		= s5pv210_lcd_controller_init,
	.enable 	= s5pv210_lcd_controller_enable,
	.disable 	= s5pv210_lcd_controller_disable,
};


int s5pv210_lcd_controller_add(void)
{
	return register_lcd_controller(&s5pv210_lcd_controller);
}

通用的初始化函数,今后如果换了别的型号的lcd,更改私有的数据部分就可以,然后注册就行。

更换了控制器,只需要重新实现一个lcd_controller就行,整体框架不需要改变。

void lcd_init(void)
{

	/* 注册lcd */
	lcd_7_0_add();

	/* 注册lcd控制器 */
	lcd_controller_add();

	/* 选择lcd */
	select_lcd("lcd_7.0");

	/* 选择lcd控制器 */
	select_lcd_controller("s5pv210");

	/* 初始化lcd控制器 */
	lcd_controller_init(s_lcd_params);

	/* 开lcd */
	lcd_enable();












你可能感兴趣的:(linux驱动,嵌入式)