欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 10:35:33
...

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


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

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

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

2.S5PV210的LCD组成

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

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

我用红色框框起来了。

1.默认就使用DMA功能

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



S5PV210支持多窗口显示。

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

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

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

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

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


3.主要寄存器

VIDCON0

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

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

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

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

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

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


VIDCON1

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

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

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


下面两个是时序相关的

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

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

屏幕分辨率配置

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


WINDOW0

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

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



SHODOWCON

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

窗口1左上角坐标

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

窗口1右下角坐标

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

窗口1的大小

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


窗口1显存的起始地址


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


窗口1显存的结束地址

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

显示要经过的路径

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


源码主要分两层。


一层是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; i<LCD_NUM; i++)
	{
		if(!lcd_paramss[i])
		{
			lcd_paramss[i] = p_lcd_par;
			return i;
		}
	}
	
	return -1;
}

int  select_lcd(char *name)
{

	int i;

	for(i=0; i<LCD_NUM; i++)
	{
		if(lcd_paramss[i] && !strcmp(lcd_paramss[i]->name,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; i<LCD_CONTROLLER_NUM; i++)
	{
		if(!lcd_controllers[i])
		{
			lcd_controllers[i] = p_lcd_ctl;
			return i;
		}
	}
	
	return -1;
}

/*
 *	通过名字匹配lcd
 */
int select_lcd_controller(char *name)
{

	int i;

	for(i=0; i<LCD_CONTROLLER_NUM; i++)
	{
		if(lcd_controllers[i] && !strcmp(lcd_controllers[i]->name,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<<p_lcd_params->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();