一、Backlight背光子系统概述
LCD的背光原理主要是由核心板的一根引脚控制背光电源,一根PWM引脚控制背光亮度组成,应用程序可以通过改变PWM的频率达到改变背光亮度的目的。
二、PWM核心驱动
代码/arch/arm/plat-samsung/pwm.c
内核中需要使能“PWM device support”
System Type --> [*]PWM device support它是 pwm核心驱动,该驱动把设备和驱动没有分离开来,都写在了这个 pwm.c中,我们先看看 pwm.c中的驱动部分
static int __init pwm_init(void) { int ret; clk_scaler[0] = clk_get(NULL, "pwm-scaler0");//获取0号时钟 clk_scaler[1] = clk_get(NULL, "pwm-scaler1");//获取1号时钟 if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) { printk(KERN_ERR "%s: failed to get scaler clocks\n", __func__); return -EINVAL; } ret = platform_driver_register(&s3c_pwm_driver);//注册pwm驱动 if (ret) printk(KERN_ERR "%s: failed to add pwm driver\n", __func__); return ret; }
//s3c_pwm_driver定义 static struct platform_driver s3c_pwm_driver = { .driver = { .name = "s3c24xx-pwm",//驱动名 .owner = THIS_MODULE, }, .probe = s3c_pwm_probe,//探测函数 .remove = __devexit_p(s3c_pwm_remove), .suspend = s3c_pwm_suspend, .resume = s3c_pwm_resume, };
三、Backlight核心驱动
代码/driver/video/backlight/backlight.c
内核中需要使能“Lowlevel Backlight controls”
Device Drivers --->
Graphics support ---> [*] Backlight & LCD device support ---> <*> Lowlevel Backlight controls
代码分析
static int __init backlight_class_init(void) { backlight_class = class_create(THIS_MODULE, "backlight");//在/sys/class下注册backlight类 if (IS_ERR(backlight_class)) { printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n", PTR_ERR(backlight_class)); return PTR_ERR(backlight_class); } backlight_class->dev_attrs = bl_device_attributes;//添加类属性 backlight_class->suspend = backlight_suspend; backlight_class->resume = backlight_resume; return 0; }
backlight背光系统的主要就是靠这个类属性,设置背光值就是向这个类属性中某个成员写入背光值,这个类属性就是给用户的同一接口
#define __ATTR(_name,_mode,_show,_store) { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ }
static struct device_attribute bl_device_attributes[] = { __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power), __ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness), __ATTR(actual_brightness, 0444, backlight_show_actual_brightness, NULL), __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL), __ATTR(type, 0444, backlight_show_type, NULL), __ATTR_NULL, };很明显,在backlight类中我们创建了bl_power,brightness,actural_brightness,max_brightness四个成员,其中brightness是当前亮度,max_brightness是最大亮度。当用户层通过cat或者echo命令就会触发这些成员。对于这些属性的读写函数,我们先看看读的函数backlight_show_max_brightness吧
static ssize_t backlight_show_max_brightness(struct device *dev, struct device_attribute *attr, char *buf) { struct backlight_device *bd = to_backlight_device(dev); return sprintf(buf, "%d\n", bd->props.max_brightness);//输出最大亮度 }
这个函数很简单,但是重点是引入了几个backlight背光子系统的几个重要的数据结构,我们好好学习下。
首先是backlight背光子系统的设备结构体backlight_device
struct backlight_device { /* Backlight properties */ struct backlight_properties props;//背光属性 /* Serialise access to update_status method */ struct mutex update_lock; /* This protects the 'ops' field. If 'ops' is NULL, the driver that registered this device has been unloaded, and if class_get_devdata() points to something in the body of that driver, it is also invalid. */ struct mutex ops_lock; const struct backlight_ops *ops;//背光操作函数,类似于file_operation /* The framebuffer notifier block */ struct notifier_block fb_notif; struct device dev;//内嵌设备 };下面先看看背光属性结构体backlight_properties
struct backlight_properties { /* Current User requested brightness (0 - max_brightness) */ int brightness;//当前背光值 /* Maximal value for brightness (read-only) */ int max_brightness;//最大背光值 /* Current FB Power mode (0: full on, 1..3: power saving modes; 4: full off), see FB_BLANK_XXX */ int power; /* FB Blanking active? (values as for power) */ /* Due to be removed, please use (state & BL_CORE_FBBLANK) */ int fb_blank; /* Backlight type */ enum backlight_type type; /* Flags used to signal drivers of state changes */ /* Upper 4 bits are reserved for driver internal use */ unsigned int state; #define BL_CORE_SUSPENDED (1 << 0) /* backlight is suspended */ #define BL_CORE_FBBLANK (1 << 1) /* backlight is under an fb blank event */ #define BL_CORE_DRIVER4 (1 << 28) /* reserved for driver specific use */ #define BL_CORE_DRIVER3 (1 << 29) /* reserved for driver specific use */ #define BL_CORE_DRIVER2 (1 << 30) /* reserved for driver specific use */ #define BL_CORE_DRIVER1 (1 << 31) /* reserved for driver specific use */ };
在看看背光操作函数
struct backlight_ops { unsigned int options; #define BL_CORE_SUSPENDRESUME (1 << 0) /* Notify the backlight driver some property has changed */ int (*update_status)(struct backlight_device *);//更新背光状态 /* Return the current backlight brightness (accounting for power, fb_blank etc.) */ int (*get_brightness)(struct backlight_device *);//获取背光值 /* Check if given framebuffer device is the one bound to this backlight; return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */ int (*check_fb)(struct backlight_device *, struct fb_info *); };当前背光值函数 backlight_store_brightness
static ssize_t backlight_store_brightness(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc; struct backlight_device *bd = to_backlight_device(dev); unsigned long brightness; rc = strict_strtoul(buf, 0, &brightness); if (rc) return rc; rc = -ENXIO; mutex_lock(&bd->ops_lock); if (bd->ops) { if (brightness > bd->props.max_brightness) rc = -EINVAL; else { pr_debug("backlight: set brightness to %lu\n", brightness); bd->props.brightness = brightness;//传入背光值 backlight_update_status(bd);//更新背光状态 rc = count; } } mutex_unlock(&bd->ops_lock); backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); return rc; }
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); }对于这个backlight背光核心层驱动backlight.c,剩下的就是这个pwm.c给我们提供了哪些接口函数了
struct backlight_device *backlight_device_register(const char *name, struct device *parent, void *devdata, struct backlight_ops *ops) void backlight_device_unregister(struct backlight_device *bd) EXPORT_SYMBOL(backlight_device_register); //注册背光设备 EXPORT_SYMBOL(backlight_device_unregister); //注销背光设备
四、基于PWM&Backlight的蜂鸣器驱动
结合上面的 PWM核心层和 Backlight背光子系统核心层,根据基于 pwm的背光驱动 /driver/video/backlight/pwm_bl.c来修改成基于 tq210的蜂鸣器驱动内核中需要使能“Generic PWM based Backlight Driver”
Device Drivers --->
Graphics support ---> [*] Backlight & LCD device support ---> <*> Generic PWM based Backlight Driver
tq210蜂鸣器使用GPD0_1口,该端口工作在TOU0模式下,就可以通过设备定时器的TCNT和TCMP来控制定时器的波形。
首先,在tq210的BSP文件mach-tq210.c,如下添加
static struct platform_device tq210_backlight_device = { .name = "pwm-backlight",//设备名 .dev = { .parent = &s3c_device_timer[0].dev,//该设备基于pwm中的0号定时器 .platform_data = &tq_backlight_data, }, };
添加平台数据
static struct platform_pwm_backlight_data tq210_backlight_data = { .pwm_id = 0,//对应的就是Timer0 .max_brightness = 255,//最大亮度 .dft_brightness = 100,//255,//当前亮度 .lth_brightness = 50,//咱不知道干啥用的 .pwm_period_ns = 20000,//78770,//T0,即输出时钟周期 .init = tq210_backlight_init,//端口初始化 .exit = tq210_backlight_exit, };
static int tq210_backlight_init(struct device *dev) { int ret; ret = gpio_request(S5PV210_GPD0(0), "Backlight"); if (ret) { printk(KERN_ERR "failed to request GPD for PWM-OUT 0\n"); return ret; } /* Configure GPIO pin with S5PV210_GPD_0_0_TOUT_0 */ s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_SFN(2)); return 0; } static void tq210_backlight_exit(struct device *dev) { s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_OUTPUT); gpio_free(S5PV210_GPD0(0)); }
然后把tq210_backlight_device添加到tq210_devices数组
static struct platform_device *tq210_devices[] __initdata = { ... #ifdef CONFIG_BACKLIGHT_PWM &s3c_device_timer[0], &s3c_device_timer[1], &s3c_device_timer[2], &s3c_device_timer[3], &tq210_backlight_device,//同时添加对应的s3c_device_timer[0] #endif ... };
最后添加头文件
#include <linux/pwm_backlight.h>