Android LCD(四):LCD驱动调试篇

关键词:android LCD TFTSN75LVDS83B  TTL-LVDS LCD电压背光电压
平台信息:
内核:linux2.6/linux3.0
系统:android/android4.0 
平台:samsung exynos 4210、exynos 4412 、exynos 5250

作者:xubin341719(欢迎转载,请注明作者)
欢迎指正错误,共同学习、共同进步!!
下载链接:LCD规格书(404份),之前工作用用到的 、 LCD规格书00  、 LCD规格书01   、   LCD测试图片,彩条灰阶等  

Android LCD(一):LCD基本原理篇
Android LCD(二):LCD常用接口原理篇
Android LCD(三):Samsung LCD接口篇
Android LCD(四):LCD驱动调试篇
这篇我们以一个实例来说明,Samsung Exynos4412搭配TTL转LVDS芯片SN75LVDS83B、LVDS接口LCD为例说明。从硬件接口、驱动配置、背光PWM调节三部分说明。

下载:SN75LVDS83B规格书
一、LCD接口原理以及硬件电路
Samsung Exynos4412、SN75LVDS83B、LVDS接口LCD(24bit)为例说明,三者的关系如下:


如上图所示,我们在应用中我,主控(Exynos4412)输出RGB信号到TFT-LCD大体经过三部分:
1)、标号1部分,主控(Exynos4412)输出TTL信号;
2)、标号2部分,TTLRGB-LVDS转换芯片SN75LVDS83B,把TTL信号转换成LVDS信号,传输到显示器的LVDS接收端;这部分有SN75LVDS83B编码芯片自动完成,所以我们不需要程序控制;
3)、标号3部分,分两个小部分,LVDS转换成TTLTFT-LCD显示部分;我们前面说过,TFT-LCD其实只识别TTL信号,所以要有一个转换的过程,先把LVDS信号转换、解码成TTL信号,在TFT-LCD上显示。
有上面的过程,其实我们关心调试的部分只有标号1部分到标号2部分,后面标号2到标号3的部分是自动完成的,不需要我们程序上控制,把标号2部分、标号3部分合并:


标号二部分可以理解为一个TTLRGB)接口的LCD,如下图所示,标号一部分就是主控信号输出端,简化图如下所示:

扫描二维码关注公众号,回复: 1063209 查看本文章


其实最简单的做法就是找个TTL接口的TFT-LCD,这样直接接上就可以。下面我们看下硬件上的电路连接:这个和我们上篇用的相同。


    有上面图可以看出:硬件连接

网络标号

说明

管脚

XvVD[0:23]

XvVDEN

XvVSYNC

XvHSYNC

XvVCLK

RGB数据、使能、行场同步、时钟信号

 这是TTL信号输出

LCD_PWM

调节背光

XpwmTOUT1/LCD_PWM/GPD0_1

LCD_LED_EN

LCD电压(TFT电压)使能

GPC1_2

LED_BL_EN

LED背光使能

GPL2_4

上面可分为几部分,电路连接部分分析:

(1)、TTL数据部分
这张图有木有烂掉呀,哈哈,就是这些数据了。还有有木有想起来摄像头的数据(ITU接口)也是这样的??其实视频这种信号的原理是通用的,所以LCD通了,摄像头也就知道怎么回事了。


2)、PWM背光调节
 PWM其实也是芯片的一个功能模块,看到他的管脚就是一个复用脚XpwmTOUT1/LCD_PWM/GPD0_1。上一篇我们粗略的了解了PWM,就是用到这里。但是有一个疑问,PWM是调节背光电压的,背光电压一般都是12V以上的,我们PWM只有0-3V的样子,Exynos4412IO只有1.8V。怎么调节电压???


    其实这个PWM只是给LCDPWM控制部分,真正的电压还是通过LCD控制板上的电路实现。

3 )、 LED 背光、 LCD 电压控制
a 、背光:LED+
我们可以看到这个升压电路,通过 SY7208 VBATT 升压到 18V ,供给 LED 背光。 SY7208 最大升压 26V 。这个电压是提供给我们前面讲的背光的,也就是 CCFL 灯管或者 LED 背光组的电压。


b、LCD电压
这个电压也就是给你我们TFT阵列组用的,控制LCD液晶元素。
这部分电路分析完成,我们就有比较清晰的思路出,要一个LCD工作,要完成两部分内容:LCD上电控制,背光、LCD电压;信号输出。
二、LCD 驱动部分调试
    LCD这部分,像上篇我们说的frambuffer这些部分一般平台都是可以用的,除非你是芯片厂的要写这部分。一般公司拿到的demo板子这部分都是通的,只是针对自己的lCD换一些参数。
下面我们针对三星平台我们调试LCD的时时候程序方面的改动:
1、屏参数的配置
/kernel/drivers/video/Samsung/s3cfb_wa101s.c

  1. static struct s3cfb_lcd wa101 = {  
  2.       
  3.     .width  = 1280,//LCD 分辨率宽1280  
  4.     .height = 800, //LCD 分辨率高 800  
  5.     .bpp    = 24,//CLD 数据位 24bit  
  6.     .freq   = 60,//LCD 像素时钟 60MHz  
  7.     .timing = {//LCD porch无效值  
  8.         .h_fp   = 70,  
  9.         .h_bp   = 70,  
  10.         .h_sw   = 20,  
  11.       
  12.         .v_fp   = 10,  
  13.         .v_fpe  = 0,  
  14.         .v_bp   = 10,  
  15.         .v_bpe  = 0,  
  16.         .v_sw   = 3,  
  17.     },  
  18.   
  19.     .polarity = {//时钟、行场的极性;  
  20.         .rise_vclk  = 1,  
  21.         .inv_hsync  = 1,  
  22.         .inv_vsync  = 1,  
  23.         .inv_vden   = 0,  
  24.     },  
  25. };  
  26. /* name should be fixed as 's3cfb_set_lcd_info' */  
  27. void s3cfb_set_lcd_info(struct s3cfb_global *ctrl)//初始化结构体  
  28. {  
  29.     wa101.init_ldi = NULL;  
  30.     ctrl->lcd = &wa101;  
  31. #endif  
  32. }  
static struct s3cfb_lcd wa101 = {
	
	.width	= 1280,//LCD 分辨率宽1280
	.height = 800, //LCD 分辨率高 800
	.bpp	= 24,//CLD 数据位 24bit
	.freq	= 60,//LCD 像素时钟 60MHz
	.timing = {//LCD porch无效值
		.h_fp	= 70,
		.h_bp	= 70,
		.h_sw	= 20,
	
		.v_fp	= 10,
		.v_fpe	= 0,
		.v_bp	= 10,
		.v_bpe	= 0,
		.v_sw	= 3,
	},

	.polarity = {//时钟、行场的极性;
		.rise_vclk	= 1,
		.inv_hsync	= 1,
		.inv_vsync	= 1,
		.inv_vden	= 0,
	},
};
/* name should be fixed as 's3cfb_set_lcd_info' */
void s3cfb_set_lcd_info(struct s3cfb_global *ctrl)//初始化结构体
{
	wa101.init_ldi = NULL;
	ctrl->lcd = &wa101;
#endif
}
还能想起上一篇的如何阅读规格书中的那些参数不,把这些填入就可以。
2、数据管脚初始化


kernel/arch/arm/mach-exynos/setup-fb-s5p.c

  1. void s3cfb_cfg_gpio(struct platform_device *pdev)  
  2. {  
  3.     s3cfb_gpio_setup_24bpp(EXYNOS4_GPF0(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);  
  4.     s3cfb_gpio_setup_24bpp(EXYNOS4_GPF1(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);  
  5.     s3cfb_gpio_setup_24bpp(EXYNOS4_GPF2(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);  
  6.     s3cfb_gpio_setup_24bpp(EXYNOS4_GPF3(0), 4, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);  
  7. }  
void s3cfb_cfg_gpio(struct platform_device *pdev)
{
	s3cfb_gpio_setup_24bpp(EXYNOS4_GPF0(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);
	s3cfb_gpio_setup_24bpp(EXYNOS4_GPF1(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);
	s3cfb_gpio_setup_24bpp(EXYNOS4_GPF2(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);
	s3cfb_gpio_setup_24bpp(EXYNOS4_GPF3(0), 4, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);
}

LCD 数据脚初始化,驱动能力设为最高 S5P_GPIO_DRVSTR_LV4 ;管脚驱动能力, S5P_GPIO_DRVSTR_LV1-4 四个等级选择。
3、 时钟控制部分
kernel/arch/arm/mach-exynos/setup-fb-s5p.c

  1. int s3cfb_clk_on(struct platform_device *pdev, struct clk **s3cfb_clk)  
  2. {  
  3.     struct clk *sclk = NULL;  
  4.     struct clk *mout_mpll = NULL;  
  5.     struct clk *lcd_clk = NULL;  
  6.   
  7.     u32 rate = 0;  
  8.     int ret = 0;  
  9.   
  10.     lcd_clk = clk_get(&pdev->dev, "lcd");  
  11.     if (IS_ERR(lcd_clk)) {  
  12.         dev_err(&pdev->dev, "failed to get operation clk for fimd\n");  
  13.         goto err_clk0;  
  14.     }  
  15.   
  16.     ret = clk_enable(lcd_clk);  
  17.     if (ret < 0) {  
  18.         dev_err(&pdev->dev, "failed to clk_enable of lcd clk for fimd\n");  
  19.         goto err_clk0;  
  20.     }  
  21.     clk_put(lcd_clk);  
  22.   
  23.     sclk = clk_get(&pdev->dev, "sclk_fimd");  
  24.     if (IS_ERR(sclk)) {  
  25.         dev_err(&pdev->dev, "failed to get sclk for fimd\n");  
  26.         goto err_clk1;  
  27.     }  
  28.   
  29.     if (soc_is_exynos4210())  
  30.         mout_mpll = clk_get(&pdev->dev, "mout_mpll");  
  31.     else  
  32.         mout_mpll = clk_get(&pdev->dev, "mout_mpll_user");  
  33.   
  34.     if (IS_ERR(mout_mpll)) {  
  35.         dev_err(&pdev->dev, "failed to get mout_mpll for fimd\n");  
  36.         goto err_clk2;  
  37.     }  
  38.   
  39.     ret = clk_set_parent(sclk, mout_mpll);  
  40.     if (ret < 0) {  
  41.         dev_err(&pdev->dev, "failed to clk_set_parent for fimd\n");  
  42.         goto err_clk2;  
  43.     }  
  44.   
  45.     if ((soc_is_exynos4412()) && (samsung_rev() >= EXYNOS4412_REV_2_0))  
  46.         ret = clk_set_rate(sclk, 880000000);  
  47.     else  
  48.         ret = clk_set_rate(sclk, 800000000);  
  49.     if (ret < 0) {  
  50.         dev_err(&pdev->dev, "failed to clk_set_rate of sclk for fimd\n");  
  51.         goto err_clk2;  
  52.     }  
  53.     dev_dbg(&pdev->dev, "set fimd sclk rate to %d\n", rate);  
  54.   
  55.     clk_put(mout_mpll);  
  56.   
  57.     ret = clk_enable(sclk);  
  58.     if (ret < 0) {  
  59.         dev_err(&pdev->dev, "failed to clk_enable of sclk for fimd\n");  
  60.         goto err_clk2;  
  61.     }  
  62.   
  63.     *s3cfb_clk = sclk;  
  64.   
  65.     return 0;  
  66.   
  67. err_clk2:  
  68.     clk_put(mout_mpll);  
  69. err_clk1:  
  70.     clk_put(sclk);  
  71. err_clk0:  
  72.     clk_put(lcd_clk);  
  73.   
  74.     return -EINVAL;  
  75. }  
  76. int s3cfb_clk_off(struct platform_device *pdev, struct clk **clk)  
  77. {  
  78.     struct clk *lcd_clk = NULL;  
  79.   
  80.     lcd_clk = clk_get(&pdev->dev, "lcd");  
  81.     if (IS_ERR(lcd_clk)) {  
  82.         printk(KERN_ERR "failed to get ip clk for fimd0\n");  
  83.         goto err_clk0;  
  84.     }  
  85.   
  86.     clk_disable(lcd_clk);  
  87.     clk_put(lcd_clk);  
  88.   
  89.     clk_disable(*clk);  
  90.     clk_put(*clk);  
  91.   
  92.     *clk = NULL;  
  93.   
  94.     return 0;  
  95.   
  96. err_clk0:  
  97.     clk_put(lcd_clk);  
  98.   
  99.     return -EINVAL;  
  100. }  
  101.   
  102. void s3cfb_get_clk_name(char *clk_name)  
  103. {  
  104.     strcpy(clk_name, "sclk_fimd");  
  105. }  
int s3cfb_clk_on(struct platform_device *pdev, struct clk **s3cfb_clk)
{
	struct clk *sclk = NULL;
	struct clk *mout_mpll = NULL;
	struct clk *lcd_clk = NULL;

	u32 rate = 0;
	int ret = 0;

	lcd_clk = clk_get(&pdev->dev, "lcd");
	if (IS_ERR(lcd_clk)) {
		dev_err(&pdev->dev, "failed to get operation clk for fimd\n");
		goto err_clk0;
	}

	ret = clk_enable(lcd_clk);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to clk_enable of lcd clk for fimd\n");
		goto err_clk0;
	}
	clk_put(lcd_clk);

	sclk = clk_get(&pdev->dev, "sclk_fimd");
	if (IS_ERR(sclk)) {
		dev_err(&pdev->dev, "failed to get sclk for fimd\n");
		goto err_clk1;
	}

	if (soc_is_exynos4210())
		mout_mpll = clk_get(&pdev->dev, "mout_mpll");
	else
		mout_mpll = clk_get(&pdev->dev, "mout_mpll_user");

	if (IS_ERR(mout_mpll)) {
		dev_err(&pdev->dev, "failed to get mout_mpll for fimd\n");
		goto err_clk2;
	}

	ret = clk_set_parent(sclk, mout_mpll);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to clk_set_parent for fimd\n");
		goto err_clk2;
	}

	if ((soc_is_exynos4412()) && (samsung_rev() >= EXYNOS4412_REV_2_0))
		ret = clk_set_rate(sclk, 880000000);
	else
		ret = clk_set_rate(sclk, 800000000);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to clk_set_rate of sclk for fimd\n");
		goto err_clk2;
	}
	dev_dbg(&pdev->dev, "set fimd sclk rate to %d\n", rate);

	clk_put(mout_mpll);

	ret = clk_enable(sclk);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to clk_enable of sclk for fimd\n");
		goto err_clk2;
	}

	*s3cfb_clk = sclk;

	return 0;

err_clk2:
	clk_put(mout_mpll);
err_clk1:
	clk_put(sclk);
err_clk0:
	clk_put(lcd_clk);

	return -EINVAL;
}
int s3cfb_clk_off(struct platform_device *pdev, struct clk **clk)
{
	struct clk *lcd_clk = NULL;

	lcd_clk = clk_get(&pdev->dev, "lcd");
	if (IS_ERR(lcd_clk)) {
		printk(KERN_ERR "failed to get ip clk for fimd0\n");
		goto err_clk0;
	}

	clk_disable(lcd_clk);
	clk_put(lcd_clk);

	clk_disable(*clk);
	clk_put(*clk);

	*clk = NULL;

	return 0;

err_clk0:
	clk_put(lcd_clk);

	return -EINVAL;
}

void s3cfb_get_clk_name(char *clk_name)
{
	strcpy(clk_name, "sclk_fimd");
}
4、背光、LCD电压的控制


LCD_LED_EN

LCD电压(TFT电压)使能

GPC1_2

LED_BL_EN

LED背光使能

GPL2_4


  1. int s3cfb_backlight_on(struct platform_device *pdev)  
  2. {  
  3.         int err;  
  4.         pwm_set();  
  5.     err = gpio_request_one(EXYNOS4_GPL2(4), GPIOF_OUT_INIT_HIGH, "GPL2_4");  
  6.     if (err) {  
  7.         printk(KERN_ERR "failed to request GPL2 for "  
  8.             "lcd backlight control\n");  
  9.         return err;  
  10.     }  
  11.     s3c_gpio_setpull(EXYNOS4_GPL2(4),S3C_GPIO_PULL_NONE);  
  12.     gpio_direction_output(EXYNOS4_GPL2(4), 1);  
  13.     gpio_free(EXYNOS4_GPL2(4));  
  14.     mdelay(20);  
  15.     err = gpio_request_one(EXYNOS4_GPC1(2), GPIOF_OUT_INIT_HIGH, "GPC1_2");  
  16.     if (err) {  
  17.         printk(KERN_ERR "failed to request GPC1 for "  
  18.             "lcd backlight control\n");  
  19.         return err;  
  20.     }  
  21.     s3c_gpio_setpull(EXYNOS4_GPC1(2),S3C_GPIO_PULL_NONE);  
  22.     gpio_direction_output(EXYNOS4_GPC1(2), 0);  
  23.     gpio_free(EXYNOS4_GPC1(2));  
  24.   
  25.     mdelay(20);  
  26.     err = gpio_request(EXYNOS4_GPD0(1), "GPD0_1");  
  27.   
  28.     if (err) {  
  29.         printk(KERN_ERR "failed to request GPD0_1 for "  
  30.             "lcd pwm control\n");  
  31.         return err;  
  32.     }  
  33.     s3c_gpio_setpull(EXYNOS4_GPD0(1),S3C_GPIO_PULL_NONE);  
  34.     s5p_gpio_set_drvstr(EXYNOS4_GPD0(1), S5P_GPIO_DRVSTR_LV4);    
  35.     gpio_direction_output(EXYNOS4_GPD0(1), 1);  
  36.     s3c_gpio_cfgpin(EXYNOS4_GPD0(1), EXYNOS4_GPD_0_1_TOUT_1);  
  37.       
  38.     gpio_free(EXYNOS4_GPD0(1));  
  39.     mdelay(20);  
  40.     return 0;  
  41.   
  42.     return 0;  
  43. }  
  44.   
  45. int s3cfb_backlight_off(struct platform_device *pdev)  
  46. {  
  47.     int err;  
  48.     err = gpio_request_one(EXYNOS4_GPL2(4), GPIOF_OUT_INIT_LOW, "GPL2_4");  
  49.     if (err) {  
  50.         printk(KERN_ERR "failed to request GPL2 for "  
  51.             "lcd backlight control\n");  
  52.         return err;  
  53.     }  
  54.     s3c_gpio_setpull(EXYNOS4_GPL2(4),S3C_GPIO_PULL_NONE);  
  55.     gpio_direction_output(EXYNOS4_GPL2(4), 0);  
  56.     gpio_free(EXYNOS4_GPL2(4));  
  57.   
  58.     err = gpio_request_one(EXYNOS4_GPC1(2), GPIOF_OUT_INIT_HIGH, "GPC1_2");  
  59.     if (err) {  
  60.         printk(KERN_ERR "failed to request GPC1 for "  
  61.             "lcd backlight control\n");  
  62.         return err;  
  63.     }  
  64.     gpio_free(EXYNOS4_GPC1(2));  
  65.   
  66.     return 0;  
  67. }  
int s3cfb_backlight_on(struct platform_device *pdev)
{
        int err;
      	pwm_set();
	err = gpio_request_one(EXYNOS4_GPL2(4), GPIOF_OUT_INIT_HIGH, "GPL2_4");
	if (err) {
		printk(KERN_ERR "failed to request GPL2 for "
			"lcd backlight control\n");
		return err;
	}
	s3c_gpio_setpull(EXYNOS4_GPL2(4),S3C_GPIO_PULL_NONE);
	gpio_direction_output(EXYNOS4_GPL2(4), 1);
	gpio_free(EXYNOS4_GPL2(4));
	mdelay(20);
	err = gpio_request_one(EXYNOS4_GPC1(2), GPIOF_OUT_INIT_HIGH, "GPC1_2");
	if (err) {
		printk(KERN_ERR "failed to request GPC1 for "
			"lcd backlight control\n");
		return err;
	}
	s3c_gpio_setpull(EXYNOS4_GPC1(2),S3C_GPIO_PULL_NONE);
	gpio_direction_output(EXYNOS4_GPC1(2), 0);
	gpio_free(EXYNOS4_GPC1(2));

	mdelay(20);
	err = gpio_request(EXYNOS4_GPD0(1), "GPD0_1");

	if (err) {
		printk(KERN_ERR "failed to request GPD0_1 for "
			"lcd pwm control\n");
		return err;
	}
	s3c_gpio_setpull(EXYNOS4_GPD0(1),S3C_GPIO_PULL_NONE);
	s5p_gpio_set_drvstr(EXYNOS4_GPD0(1), S5P_GPIO_DRVSTR_LV4);	
	gpio_direction_output(EXYNOS4_GPD0(1), 1);
	s3c_gpio_cfgpin(EXYNOS4_GPD0(1), EXYNOS4_GPD_0_1_TOUT_1);
	
	gpio_free(EXYNOS4_GPD0(1));
	mdelay(20);
	return 0;

	return 0;
}

int s3cfb_backlight_off(struct platform_device *pdev)
{
	int err;
	err = gpio_request_one(EXYNOS4_GPL2(4), GPIOF_OUT_INIT_LOW, "GPL2_4");
	if (err) {
		printk(KERN_ERR "failed to request GPL2 for "
			"lcd backlight control\n");
		return err;
	}
	s3c_gpio_setpull(EXYNOS4_GPL2(4),S3C_GPIO_PULL_NONE);
	gpio_direction_output(EXYNOS4_GPL2(4), 0);
	gpio_free(EXYNOS4_GPL2(4));

	err = gpio_request_one(EXYNOS4_GPC1(2), GPIOF_OUT_INIT_HIGH, "GPC1_2");
	if (err) {
		printk(KERN_ERR "failed to request GPC1 for "
			"lcd backlight control\n");
		return err;
	}
	gpio_free(EXYNOS4_GPC1(2));

	return 0;
}

5、PWM的设置

arch/arm/mach-exynos/mach-smdk4x12.c


  1. Arch/arm/mach-exynos/mach-smdk4x12.c  
  2. /* LCD Backlight data */  
  3. static struct samsung_bl_gpio_info smdk4x12_bl_gpio_info = {  
  4.     .no = EXYNOS4_GPD0(1),//PWM管脚XpwmTOUT1/LCD_PWM/GPD0_1  
  5.     .func = S3C_GPIO_SFN(2),  
  6. };  
  7. static struct platform_pwm_backlight_data smdk4x12_bl_data = {  
  8.     .pwm_id = 1,//PWM ID PWM编号为1号;  
  9.     .pwm_period_ns  = 90000,  //22k  
  10. };  
  11. static void __init smdk4x12_machine_init(void)  
  12. {     
  13. ………………  
  14. samsung_bl_set(&smdk4x12_bl_gpio_info, &smdk4x12_bl_data);//在初始化的时候把对应的结构体初始化  
  15. ………………  
  16. }  
Arch/arm/mach-exynos/mach-smdk4x12.c
/* LCD Backlight data */
static struct samsung_bl_gpio_info smdk4x12_bl_gpio_info = {
	.no = EXYNOS4_GPD0(1),//PWM管脚XpwmTOUT1/LCD_PWM/GPD0_1
	.func = S3C_GPIO_SFN(2),
};
static struct platform_pwm_backlight_data smdk4x12_bl_data = {
	.pwm_id = 1,//PWM ID PWM编号为1号;
	.pwm_period_ns  = 90000,  //22k
};
static void __init smdk4x12_machine_init(void)
{	
………………
samsung_bl_set(&smdk4x12_bl_gpio_info, &smdk4x12_bl_data);//在初始化的时候把对应的结构体初始化
………………
}

samsung_bl_set看下这个函数的实现:

kernel/arch/arm/palt-samsung/dev-backlight.c

  1. void samsung_bl_set(struct samsung_bl_gpio_info *gpio_info,  
  2.     struct platform_pwm_backlight_data *bl_data)  
  3. {  
  4.     int ret = 0;  
  5.     struct platform_device *samsung_bl_device;  
  6.     struct platform_pwm_backlight_data *samsung_bl_data;  
  7.   
  8.     samsung_bl_device = kmemdup(&samsung_dfl_bl_device,  
  9.             sizeof(struct platform_device), GFP_KERNEL);//(1)、分配内存空间;  
  10.     if (!samsung_bl_device) {  
  11.         printk(KERN_ERR "%s: no memory for platform dev\n", __func__);  
  12.         return;  
  13.     }  
  14.   
  15.     samsung_bl_data = s3c_set_platdata(&samsung_dfl_bl_data,  
  16.         sizeof(struct platform_pwm_backlight_data), samsung_bl_device);//(2)、  
  17.     if (!samsung_bl_data) {  
  18.         printk(KERN_ERR "%s: no memory for platform dev\n", __func__);  
  19.         goto err_data;  
  20.     }  
  21.   
  22.     /* Copy board specific data provided by user */  
  23.     samsung_bl_data->pwm_id = bl_data->pwm_id;//(3)、把具体配置的数据给samsung_bl_data  
  24.     samsung_bl_device->dev.parent =  
  25.             &s3c_device_timer[samsung_bl_data->pwm_id].dev;  
  26.   
  27.     if (bl_data->max_brightness)//(4)、对bl_data的结构体检查,如果没有复制则用default的值  
  28.         samsung_bl_data->max_brightness = bl_data->max_brightness;  
  29.     if (bl_data->dft_brightness)  
  30.         samsung_bl_data->dft_brightness = bl_data->dft_brightness;  
  31.     if (bl_data->lth_brightness)  
  32.         samsung_bl_data->lth_brightness = bl_data->lth_brightness;  
  33.     if (bl_data->pwm_period_ns)  
  34.         samsung_bl_data->pwm_period_ns = bl_data->pwm_period_ns;  
  35.     if (bl_data->init)  
  36.         samsung_bl_data->init = bl_data->init;  
  37.     if (bl_data->notify)  
  38.         samsung_bl_data->notify = bl_data->notify;  
  39.     if (bl_data->exit)  
  40.         samsung_bl_data->exit = bl_data->exit;  
  41.     if (bl_data->check_fb)  
  42.         samsung_bl_data->check_fb = bl_data->check_fb;  
  43.   
  44.     /* Keep the GPIO info for future use */  
  45.     s3c_device_timer[samsung_bl_data->pwm_id].dev.platform_data = gpio_info;  
  46.   
  47.     /* Register the specific PWM timer dev for Backlight control */  
  48.     ret = platform_device_register(//(5)、注册PWM设备驱动;  
  49.             &s3c_device_timer[samsung_bl_data->pwm_id]);  
  50.     if (ret) {  
  51.         printk(KERN_ERR "failed to register pwm timer for backlight: %d\n", ret);  
  52.         goto err_plat_reg1;  
  53.     }  
  54.   
  55.     /* Register the Backlight dev */  
  56.     ret = platform_device_register(samsung_bl_device);//(6)、注册背光设备驱动;  
  57.     if (ret) {  
  58.         printk(KERN_ERR "failed to register backlight device: %d\n", ret);  
  59.         goto err_plat_reg2;  
  60.     }  
  61.   
  62.     return;  
  63.   
  64. err_plat_reg2://(7)、如果有异常的情况下退出;  
  65.     platform_device_unregister(&s3c_device_timer[samsung_bl_data->pwm_id]);  
  66. err_plat_reg1:  
  67.     kfree(samsung_bl_data);  
  68. err_data:  
  69.     kfree(samsung_bl_device);  
  70.     return;  
  71. }  
void samsung_bl_set(struct samsung_bl_gpio_info *gpio_info,
	struct platform_pwm_backlight_data *bl_data)
{
	int ret = 0;
	struct platform_device *samsung_bl_device;
	struct platform_pwm_backlight_data *samsung_bl_data;

	samsung_bl_device = kmemdup(&samsung_dfl_bl_device,
			sizeof(struct platform_device), GFP_KERNEL);//(1)、分配内存空间;
	if (!samsung_bl_device) {
		printk(KERN_ERR "%s: no memory for platform dev\n", __func__);
		return;
	}

	samsung_bl_data = s3c_set_platdata(&samsung_dfl_bl_data,
		sizeof(struct platform_pwm_backlight_data), samsung_bl_device);//(2)、
	if (!samsung_bl_data) {
		printk(KERN_ERR "%s: no memory for platform dev\n", __func__);
		goto err_data;
	}

	/* Copy board specific data provided by user */
	samsung_bl_data->pwm_id = bl_data->pwm_id;//(3)、把具体配置的数据给samsung_bl_data
	samsung_bl_device->dev.parent =
			&s3c_device_timer[samsung_bl_data->pwm_id].dev;

	if (bl_data->max_brightness)//(4)、对bl_data的结构体检查,如果没有复制则用default的值
		samsung_bl_data->max_brightness = bl_data->max_brightness;
	if (bl_data->dft_brightness)
		samsung_bl_data->dft_brightness = bl_data->dft_brightness;
	if (bl_data->lth_brightness)
		samsung_bl_data->lth_brightness = bl_data->lth_brightness;
	if (bl_data->pwm_period_ns)
		samsung_bl_data->pwm_period_ns = bl_data->pwm_period_ns;
	if (bl_data->init)
		samsung_bl_data->init = bl_data->init;
	if (bl_data->notify)
		samsung_bl_data->notify = bl_data->notify;
	if (bl_data->exit)
		samsung_bl_data->exit = bl_data->exit;
	if (bl_data->check_fb)
		samsung_bl_data->check_fb = bl_data->check_fb;

	/* Keep the GPIO info for future use */
	s3c_device_timer[samsung_bl_data->pwm_id].dev.platform_data = gpio_info;

	/* Register the specific PWM timer dev for Backlight control */
	ret = platform_device_register(//(5)、注册PWM设备驱动;
			&s3c_device_timer[samsung_bl_data->pwm_id]);
	if (ret) {
		printk(KERN_ERR "failed to register pwm timer for backlight: %d\n", ret);
		goto err_plat_reg1;
	}

	/* Register the Backlight dev */
	ret = platform_device_register(samsung_bl_device);//(6)、注册背光设备驱动;
	if (ret) {
		printk(KERN_ERR "failed to register backlight device: %d\n", ret);
		goto err_plat_reg2;
	}

	return;

err_plat_reg2://(7)、如果有异常的情况下退出;
	platform_device_unregister(&s3c_device_timer[samsung_bl_data->pwm_id]);
err_plat_reg1:
	kfree(samsung_bl_data);
err_data:
	kfree(samsung_bl_device);
	return;
}

(1)、分配内存空间

  1.     samsung_bl_device = kmemdup(&samsung_dfl_bl_device,  
  2.             sizeof(struct platform_device), GFP_KERNEL);  
  3. 其中:  
  4. static struct platform_pwm_backlight_data samsung_dfl_bl_data = {  
  5.     .max_brightness = 255,  
  6.     .dft_brightness = 140,   
  7.     .pwm_period_ns  = 78770,  
  8.     .init           = samsung_bl_init,  
  9.     .exit           = samsung_bl_exit,  
  10. };  
  11.   
  12. static struct platform_device samsung_dfl_bl_device = {  
  13.     .name       = "pwm-backlight",  
  14. };  
	samsung_bl_device = kmemdup(&samsung_dfl_bl_device,
			sizeof(struct platform_device), GFP_KERNEL);
其中:
static struct platform_pwm_backlight_data samsung_dfl_bl_data = {
	.max_brightness = 255,
	.dft_brightness = 140, 
	.pwm_period_ns  = 78770,
	.init           = samsung_bl_init,
	.exit           = samsung_bl_exit,
};

static struct platform_device samsung_dfl_bl_device = {
	.name		= "pwm-backlight",
};

(2)、

(3)、把具体配置的数据给samsung_bl_data

  1. arch/arm/mach-exynos/mach-smdk4x12.c  
  2. static struct samsung_bl_gpio_info smdk4x12_bl_gpio_info = {  
  3.     .no = EXYNOS4_GPD0(1),//PWM管脚XpwmTOUT1/LCD_PWM/GPD0_1  
  4.     .func = S3C_GPIO_SFN(2),  
  5. };  
  6. static struct platform_pwm_backlight_data smdk4x12_bl_data = {  
  7.     .pwm_id = 1,//PWM ID PWM编号为1号;  
  8.     .pwm_period_ns  = 90000,  //22k  
  9. };  
arch/arm/mach-exynos/mach-smdk4x12.c
static struct samsung_bl_gpio_info smdk4x12_bl_gpio_info = {
	.no = EXYNOS4_GPD0(1),//PWM管脚XpwmTOUT1/LCD_PWM/GPD0_1
	.func = S3C_GPIO_SFN(2),
};
static struct platform_pwm_backlight_data smdk4x12_bl_data = {
	.pwm_id = 1,//PWM ID PWM编号为1号;
	.pwm_period_ns  = 90000,  //22k
};

(4)、对bl_data的结构体检查,如果没有复制则用default的值

参考(1)中的那些值。

(5)、注册PWM设备驱动;

  1. ret = platform_device_register(  
  2.         &s3c_device_timer[samsung_bl_data->pwm_id]);  
    ret = platform_device_register(
            &s3c_device_timer[samsung_bl_data->pwm_id]);
其中s3c_device_timer[]这个结构体如下:

  1. struct platform_device s3c_device_timer[] = {  
  2.     [0] = { DEFINE_S3C_TIMER(0, IRQ_TIMER0) },  
  3.     [1] = { DEFINE_S3C_TIMER(1, IRQ_TIMER1) },  
  4.     [2] = { DEFINE_S3C_TIMER(2, IRQ_TIMER2) },  
  5.     [3] = { DEFINE_S3C_TIMER(3, IRQ_TIMER3) },  
  6.     [4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) },  
struct platform_device s3c_device_timer[] = {
	[0] = { DEFINE_S3C_TIMER(0, IRQ_TIMER0) },
	[1] = { DEFINE_S3C_TIMER(1, IRQ_TIMER1) },
	[2] = { DEFINE_S3C_TIMER(2, IRQ_TIMER2) },
	[3] = { DEFINE_S3C_TIMER(3, IRQ_TIMER3) },
	[4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) },
我们饿samsung_bl_data->pwm_id=1;所以选择[1] = { DEFINE_S3C_TIMER(1, IRQ_TIMER1) },

(6)、注册背光设备驱动;

  1. ret =platform_device_register(samsung_bl_device);   
    ret =platform_device_register(samsung_bl_device); 
其中:samsung_bl_device

  1. samsung_bl_data = s3c_set_platdata(&samsung_dfl_bl_data,  
  2.     sizeof(struct platform_pwm_backlight_data), samsung_bl_device);  
	samsung_bl_data = s3c_set_platdata(&samsung_dfl_bl_data,
		sizeof(struct platform_pwm_backlight_data), samsung_bl_device);


(7)、如果有异常的情况下退出;

6、PWM_BL背光驱动分析:

Kernel/drivers/video/backlight/pwm_bl.c

1)、驱动注册:

  1. static struct platform_driver pwm_backlight_driver = {  
  2.     .driver     = {  
  3.         .name   = "pwm-backlight",  
  4.         .owner  = THIS_MODULE,  
  5.     },  
  6.     .probe      = pwm_backlight_probe,  
  7.     .remove     = pwm_backlight_remove,  
  8.     .suspend    = pwm_backlight_suspend,  
  9.     .resume     = pwm_backlight_resume,  
  10. };  
  11.   
  12. static int __init pwm_backlight_init(void)  
  13. {  
  14.     return platform_driver_register(&pwm_backlight_driver);  
  15. }  
static struct platform_driver pwm_backlight_driver = {
	.driver		= {
		.name	= "pwm-backlight",
		.owner	= THIS_MODULE,
	},
	.probe		= pwm_backlight_probe,
	.remove		= pwm_backlight_remove,
	.suspend	= pwm_backlight_suspend,
	.resume		= pwm_backlight_resume,
};

static int __init pwm_backlight_init(void)
{
	return platform_driver_register(&pwm_backlight_driver);
}

2)、probe函数分析

  1. static int pwm_backlight_probe(struct platform_device *pdev)  
  2. {  
  3.     struct backlight_properties props;  
  4.     struct platform_pwm_backlight_data *data = pdev->dev.platform_data;  
  5.     struct backlight_device *bl;  
  6.     struct pwm_bl_data *pb;  
  7.     int ret;  
  8.   
  9.     if (!data) {  
  10.         dev_err(&pdev->dev, "failed to find platform data\n");  
  11.         return -EINVAL;  
  12.     }  
  13.   
  14.     if (data->init) {  
  15.         ret = data->init(&pdev->dev);  
  16.         if (ret < 0)  
  17.             return ret;  
  18.     }  
  19.   
  20.     pb = kzalloc(sizeof(*pb), GFP_KERNEL);  
  21.     if (!pb) {  
  22.         dev_err(&pdev->dev, "no memory for state\n");  
  23.         ret = -ENOMEM;  
  24.         goto err_alloc;  
  25.     }  
  26.   
  27.     global_pb=pb;  
  28.     INIT_DELAYED_WORK_DEFERRABLE(&key_event, key_event_work);//1)、任务队列初始化;  
  29.   
  30.     pb->period = data->pwm_period_ns;//2)、pb结构体初始化;  
  31.     pb->notify = data->notify;  
  32.     pb->check_fb = data->check_fb;  
  33.     pb->lth_brightness = data->lth_brightness *  
  34.         (data->pwm_period_ns / data->max_brightness);  
  35.     pb->dev = &pdev->dev;  
  36.   
  37.     pb->pwm = pwm_request(data->pwm_id, "backlight");  
  38.     if (IS_ERR(pb->pwm)) {  
  39.         dev_err(&pdev->dev, "unable to request PWM for backlight\n");  
  40.         ret = PTR_ERR(pb->pwm);  
  41.         goto err_pwm;  
  42.     } else  
  43.         dev_dbg(&pdev->dev, "got pwm for backlight\n");  
  44.   
  45.     memset(&props, 0, sizeof(struct backlight_properties));  
  46.     props.type = BACKLIGHT_RAW;  
  47.     props.max_brightness = data->max_brightness;  
  48.     bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,  
  49.                        &pwm_backlight_ops, &props);  
  50.     if (IS_ERR(bl)) {  
  51.         dev_err(&pdev->dev, "failed to register backlight\n");  
  52.         ret = PTR_ERR(bl);  
  53.         goto err_bl;  
  54.     }  
  55.     global_bl=bl;  
  56.     bl->props.brightness = data->dft_brightness;  
  57.     backlight_update_status(bl);//3)、更新背光状态;  
  58.   
  59.     platform_set_drvdata(pdev, bl);  
  60.     return 0;  
  61.   
  62. err_bl:  
  63.     pwm_free(pb->pwm);  
  64. err_pwm:  
  65.     kfree(pb);  
  66. err_alloc:  
  67.     if (data->exit)  
  68.         data->exit(&pdev->dev);  
  69.     return ret;  
  70. }  
static int pwm_backlight_probe(struct platform_device *pdev)
{
	struct backlight_properties props;
	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
	struct backlight_device *bl;
	struct pwm_bl_data *pb;
	int ret;

	if (!data) {
		dev_err(&pdev->dev, "failed to find platform data\n");
		return -EINVAL;
	}

	if (data->init) {
		ret = data->init(&pdev->dev);
		if (ret < 0)
			return ret;
	}

	pb = kzalloc(sizeof(*pb), GFP_KERNEL);
	if (!pb) {
		dev_err(&pdev->dev, "no memory for state\n");
		ret = -ENOMEM;
		goto err_alloc;
	}

	global_pb=pb;
	INIT_DELAYED_WORK_DEFERRABLE(&key_event, key_event_work);//1)、任务队列初始化;

	pb->period = data->pwm_period_ns;//2)、pb结构体初始化;
	pb->notify = data->notify;
	pb->check_fb = data->check_fb;
	pb->lth_brightness = data->lth_brightness *
		(data->pwm_period_ns / data->max_brightness);
	pb->dev = &pdev->dev;

	pb->pwm = pwm_request(data->pwm_id, "backlight");
	if (IS_ERR(pb->pwm)) {
		dev_err(&pdev->dev, "unable to request PWM for backlight\n");
		ret = PTR_ERR(pb->pwm);
		goto err_pwm;
	} else
		dev_dbg(&pdev->dev, "got pwm for backlight\n");

	memset(&props, 0, sizeof(struct backlight_properties));
	props.type = BACKLIGHT_RAW;
	props.max_brightness = data->max_brightness;
	bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
				       &pwm_backlight_ops, &props);
	if (IS_ERR(bl)) {
		dev_err(&pdev->dev, "failed to register backlight\n");
		ret = PTR_ERR(bl);
		goto err_bl;
	}
	global_bl=bl;
	bl->props.brightness = data->dft_brightness;
	backlight_update_status(bl);//3)、更新背光状态;

	platform_set_drvdata(pdev, bl);
	return 0;

err_bl:
	pwm_free(pb->pwm);
err_pwm:
	kfree(pb);
err_alloc:
	if (data->exit)
		data->exit(&pdev->dev);
	return ret;
}

1)、任务队列初始化;

key_event_work加入key_event队列,

  1. INIT_DELAYED_WORK_DEFERRABLE(&key_event, key_event_work);  
INIT_DELAYED_WORK_DEFERRABLE(&key_event, key_event_work);

队列调度函数:

  1. static void key_event_work(struct work_struct *work)  
  2. {  
  3.     global_pb->period=90000;  
  4.     global_bl->props.brightness=global_brightness;  
  5.     backlight_update_status(global_bl);  
  6.     return ;  
  7. }  
static void key_event_work(struct work_struct *work)
{
	global_pb->period=90000;
	global_bl->props.brightness=global_brightness;
	backlight_update_status(global_bl);
	return ;
}
backlight_update_status

  1. static inline void backlight_update_status(struct backlight_device *bd)  
  2. {  
  3.     mutex_lock(&bd->update_lock);  
  4.     if (bd->ops && bd->ops->update_status)  
  5.         bd->ops->update_status(bd);  
  6.     mutex_unlock(&bd->update_lock);  
  7. }  
static inline void backlight_update_status(struct backlight_device *bd)
{
	mutex_lock(&bd->update_lock);
	if (bd->ops && bd->ops->update_status)
		bd->ops->update_status(bd);
	mutex_unlock(&bd->update_lock);
}

update_statuspwm_backlight_ops结构体重指定:

  1. static const struct backlight_ops pwm_backlight_ops = {  
  2.     .update_status  = pwm_backlight_update_status,  
  3.     …………}  
static const struct backlight_ops pwm_backlight_ops = {
	.update_status	= pwm_backlight_update_status,
	…………}

pwm_backlight_update_status我们后面分析,这个其实就是我们PWM设定实现的具体实施过程。

2)、pb结构体初始化;

  1. pb->period = data->pwm_period_ns; pb->notify = data->notify;  
  2. pb->check_fb = data->check_fb;  
  3. pb->lth_brightness = data->lth_brightness *  
  4.     (data->pwm_period_ns / data->max_brightness);  
  5. pb->dev = &pdev->dev;  
  6. pb->pwm = pwm_request(data->pwm_id, "backlight");  
	pb->period = data->pwm_period_ns;	pb->notify = data->notify;
	pb->check_fb = data->check_fb;
	pb->lth_brightness = data->lth_brightness *
		(data->pwm_period_ns / data->max_brightness);
	pb->dev = &pdev->dev;
	pb->pwm = pwm_request(data->pwm_id, "backlight");

3)、更新背光状态

  1. backlight_update_status(bl);  
	backlight_update_status(bl);

4)、PWM_SET

UI设置PWM时,会调用到驱动中的pwm_set(void)这个函数。这个函数主要在开机时使用。

  1. int pwm_set(void)  
  2. {  
  3.     int error;  
  4.     struct backlight_device *bl = global_bl;  
  5.     struct pwm_bl_data *pb = global_pb;  
  6.     printk("%s__%d\n",__func__,pb->period);  
  7.     pb->period=410000;  
  8.     backlight_update_status(bl);  
  9.     schedule_delayed_work(&key_event, 600); //调用队列,跟新亮度信息;  
  10.       
  11.     return 0;  
  12. }  
int pwm_set(void)
{
	int error;
	struct backlight_device *bl = global_bl;
	struct pwm_bl_data *pb = global_pb;
	printk("%s__%d\n",__func__,pb->period);
	pb->period=410000;
	backlight_update_status(bl);
	schedule_delayed_work(&key_event, 600);	//调用队列,跟新亮度信息;
	
	return 0;
}

5)、pwm_backlight_update_status这个就是PWM变化的具体实现,当应用层调节时,会调用到这个函数,把改变的值填入寄存器。

  1. static int pwm_backlight_update_status(struct backlight_device *bl)  
  2. {  
  3.     struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);  
  4.     int brightness = bl->props.brightness;  
  5.     int max = bl->props.max_brightness;  
  6.     //if(brightness==0)  
  7.     //  return 0;  
  8.     //printk("#####%s#%d__%d\n",__func__,pb->period,brightness);  
  9.     global_brightness=brightness;  
  10.     if (bl->props.power != FB_BLANK_UNBLANK)  
  11.         brightness = 0;  
  12.   
  13.     if (bl->props.fb_blank != FB_BLANK_UNBLANK)  
  14.         brightness = 0;  
  15.   
  16.     if (pb->notify)  
  17.         brightness = pb->notify(pb->dev, brightness);  
  18.       
  19.     if (brightness == 0) {  
  20.         pwm_config(pb->pwm, 0, pb->period);  
  21.         pwm_disable(pb->pwm);  
  22.     } else {  
  23.     #if 1  
  24.         brightness = pb->lth_brightness +  
  25.             (brightness * (pb->period - pb->lth_brightness) / max);  
  26.     #else  
  27.      brightness = pb->lth_brightness +  
  28.                         (((pb->period - pb->lth_brightness) / max) * brightness );  
  29.     #endif  
  30.           
  31.         pwm_config(pb->pwm, brightness, pb->period);//这里对PWM寄存器的具体操作;  
  32.         pwm_enable(pb->pwm);  
  33.     }  
  34.     return 0;  
  35. }  
static int pwm_backlight_update_status(struct backlight_device *bl)
{
	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
	int brightness = bl->props.brightness;
	int max = bl->props.max_brightness;
	//if(brightness==0)
	//	return 0;
	//printk("#####%s#%d__%d\n",__func__,pb->period,brightness);
	global_brightness=brightness;
	if (bl->props.power != FB_BLANK_UNBLANK)
		brightness = 0;

	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
		brightness = 0;

	if (pb->notify)
		brightness = pb->notify(pb->dev, brightness);
	
	if (brightness == 0) {
		pwm_config(pb->pwm, 0, pb->period);
		pwm_disable(pb->pwm);
	} else {
	#if 1
		brightness = pb->lth_brightness +
			(brightness * (pb->period - pb->lth_brightness) / max);
	#else
	 brightness = pb->lth_brightness +
                        (((pb->period - pb->lth_brightness) / max) * brightness );
	#endif
		
		pwm_config(pb->pwm, brightness, pb->period);//这里对PWM寄存器的具体操作;
		pwm_enable(pb->pwm);
	}
	return 0;
}


三、LCD UBOOT下的控制(待整理…………)
LCD UBOOT下的控制,这部分我们没做过,后面有机会做了再把这部分完善,或者找个机会把代码详细看看。

猜你喜欢

转载自blog.csdn.net/qq_27516841/article/details/79882195
LCD