背光控制驱动分析
1,Wled backlight
在driver/leds/目录下存放着系统背光以及LED灯的驱动程序,通常需要在驱动程序中构造好struct led_classdev结构体,用来描述当前的led设备
structled_classdev {
const char *name;
int brightness;
int max_brightness;
int flags;
…..
/* Set LED brightness level */
/* Must not sleep, use a workqueue ifneeded */
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightnessbrightness);
/* Get LED brightness level */
enum led_brightness(*brightness_get)(struct led_classdev *led_cdev);
const char *default_trigger;
…
};
name: 用来表述设备的名称,在注册到ledclass上之后将在sys/leds/目录下创建一个class
其中还需要注意的是设置背光brightness_set的实现必须是不能睡眠的,通常使用一个工作队列,在工作队列中实现设置的动作。
default_trigger是一个字符串,用来匹配由链表中哪个节点的ledclass_dev作为触发控制背光,实际是在链表中添加一个node,trigger_event的时候,根据给定的name与default匹配使用那一个节点的ledclass device作为触发。
以我们项目中的leds-qpnp驱动来分析,在probe中构造ledclass_dev结构体
led->cdev.brightness_set = qpnp_led_set; //设置背光
led->cdev.brightness_get = qpnp_led_get; //获取背光
。。。
led->cdev.name= “wled:backlight”;
最后使用led_classdev_register(&spmi->dev, &led->cdev);注册到ledclass驱动上去。
static voidqpnp_led_set(struct led_classdev *led_cdev, enum led_brightness value)
{
if (value >led->cdev.max_brightness)
value =led->cdev.max_brightness;
led->cdev.brightness = value;
schedule_work(&led->work); //schedule work
}
实际上由__qpnp_led_work()函数去设置背光。
2,LCD backlight
LCDbacklight是android的背光控制接口,它是一个通用的驱动,最终会调用到WLED的背光控制驱动设置背光。
mdss_fb.c中通过led_classdev_register注册一个led classdev,同样的先需要构造一个ledclass dev结构体,与wled不同的是它的backlight level是0~255,而wled则由panel决定。
staticstruct led_classdev backlight_led = {
.name = "lcd-backlight",
.brightness = MDSS_MAX_BL_BRIGHTNESS, //255
.brightness_set =mdss_fb_set_bl_brightness,
};
那么显然续作一个数据数量的转换。比如我们项目中wled的背光是0~4095,因此需要在代码中转换。在probe同样需要注册ledclass device。代码如下:
if (!lcd_backlight_registered) {
backlight_led.brightness =mfd->panel_info->brightness_max;
backlight_led.max_brightness =mfd->panel_info->brightness_max;
if(led_classdev_register(&pdev->dev, &backlight_led))
pr_err("led_classdev_registerfailed\n");
else
lcd_backlight_registered = 1;
}
那么,用户层设定背光lcd_backlight是怎么调用到wled backlight驱动中去的呢?首先背光level数量级不同,需要做转换,将android的backlevel转换成背光驱动需要的backlevel。通过如下的宏实现:
#defineMDSS_BRIGHT_TO_BL(out, v, bl_max, max_bright) do {\
out = (2 * (v) * (bl_max) +max_bright)\
/ (2 * max_bright);\
} while (0)
从软件框图的执行流程来看,最终backlevel是从pdata->set_backlight(pdata, temp);这条语句设置下去的。
到了这里,需要提到上面的default_trigger,它是一座桥梁,过渡到wled驱动。
DEFINE_LED_TRIGGER(bl_led_trigger);实际上这个宏的作用定义了一个struct led_trigger类型的指针变量bl_led_trigger.然后在初始化的时候将defualt trigger对应的字符串注册到trigger驱动中去,实际上就是添加到对应的链表中。并返回初始化好的bl_led_trigger
led_trigger_register_simple("bkl-trigger", & bl_led_trigger);
在drivers/video/msm/mdss/mdss_dsi_panel.c中初始化时候将mdss_dsi_panel_bl_ctrl挂在函数指针上面:
ctrl_pdata->panel_data.set_backlight= mdss_dsi_panel_bl_ctrl;在这个函数中遍历链表找到对应的ledcass dev节点,led_set_brightness去设置背光。
staticinline void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness value)
{
if (value >led_cdev->max_brightness)
value =led_cdev->max_brightness;
led_cdev->brightness = value;
if (!(led_cdev->flags &LED_SUSPENDED))
led_cdev->brightness_set(led_cdev,value);
}