想在应用层自己可调整mx31-pdk板子lcd背光,以便用户能够进行lcd亮度的调整。
该板子背光通过的电源控制芯片,飞思卡尔自己的13783进行控制,其开发包中已带了电源管理的驱动,包括背光控制的驱动,这里首先略微分析一下该背光驱动代码,然后搞明白应该如何来调节lcd背光亮度。
首先要找到关于背光控制的驱动代码,经过寻找发现有关电压控制的驱动代码的存放路径为/drivers/mxc/pmic/mc13783/pmic_light.c,浏览该代码可以发现
一.关于驱动的注册的代码
在pmic_light.c的最底部有以下代码:
static int __init pmic_light_init(void)
{
pr_debug("PMIC
Light driver loading.../n");
return
platform_driver_register(&pmic_light_driver_ldm);
}
static void __exit pmic_light_exit(void)
{
platform_driver_unregister(&pmic_light_driver_ldm);
pr_debug("PMIC
Light driver successfully unloaded/n");
}
subsys_initcall(pmic_light_init);
module_exit(pmic_light_exit);
MODULE_DESCRIPTION("PMIC_LIGHT");
从以上代码可看出这里进行了电源管理驱动pmic_light_driver_ldm的注册,而关于这个驱动的定义同样在该文件中,定义为:
static struct platform_driver
pmic_light_driver_ldm = {
.driver
= {
.name = "pmic_light",
},
.suspend
= pmic_light_suspend,
.resume
= pmic_light_resume,
.probe
= pmic_light_probe,
.remove
= pmic_light_remove,
};
其中pmic_light_probe是该驱动注册时要调用的回调函数,这个函数很重要,在这个函数里可以进行设备device的保存,以及资源的获得,以及其他的。
二.Driver驱动pmic_light_probe回调函数阅读
static
int pmic_light_probe(struct platform_device *pdev)
{
int
ret = 0;
struct
class_device *temp_class;
while
(suspend_flag == 1) {
swait++;
/*
Block if the device is suspended */
if
(wait_event_interruptible(suspendq, (suspend_flag == 0))) {
return
-ERESTARTSYS;
}
}
pmic_light_major
= register_chrdev(0, "pmic_light", &pmic_light_fops);
if
(pmic_light_major < 0) {
printk(KERN_ERR
"Unable to get a major for pmic_light/n");
return
pmic_light_major;
}
init_waitqueue_head(&suspendq);
pmic_light_class
= class_create(THIS_MODULE, "pmic_light");
if
(IS_ERR(pmic_light_class)) {
printk(KERN_ERR
"Error creating pmic_light class./n");
ret
= PTR_ERR(pmic_light_class);
goto
err_out1;
}
temp_class
= class_device_create(pmic_light_class, NULL,
MKDEV(pmic_light_major, 0),
NULL, "pmic_light");
if
(IS_ERR(temp_class)) {
printk(KERN_ERR
"Error creating pmic_light class device./n");
ret
= PTR_ERR(temp_class);
goto
err_out2;
}
ret
= pmic_light_init_reg();
if
(ret != PMIC_SUCCESS) {
goto
err_out3;
}
printk(KERN_INFO
"PMIC Light successfully loaded/n");
return
ret;
err_out3:
class_device_destroy(pmic_light_class,
MKDEV(pmic_light_major, 0));
err_out2:
class_destroy(pmic_light_class);
err_out1:
unregister_chrdev(pmic_light_major,
"pmic_light");
return
ret;
}
可以看出,register_chrdev(0,
"pmic_light", &pmic_light_fops)注册了一个字符设备,名字为pmic_ligh,同时指定了它的fileoperations结构体为pmic_light_fops,关于该结构体定义同样子该c文件中,如下:
static struct file_operations pmic_light_fops = {
.owner = THIS_MODULE,
.ioctl =
pmic_light_ioctl,
.open = pmic_light_open,
.release =
pmic_light_release,
};
可以看出该设备驱动的用户层操作接口只有三个,还包括打开和关闭的两个必须函数,真正进行操作的函数只有pmic_light_ioctl一个函数。
三.设备驱动pmic_light_ioctl函数阅读
static
int pmic_light_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
t_bklit_setting_param
*bklit_setting = NULL;
t_tcled_enable_param
*tcled_setting;
t_fun_param
*fun_param;
t_tcled_ind_param
*tcled_ind;
if
(_IOC_TYPE(cmd) != 'p')
return
-ENOTTY;
switch
(cmd) {
case
PMIC_BKLIT_TCLED_ENABLE:
pmic_bklit_tcled_master_enable();
break;
case
PMIC_BKLIT_TCLED_DISABLE:
pmic_bklit_tcled_master_disable();
break;
case
PMIC_BKLIT_ENABLE:
pmic_bklit_master_enable();
break;
case
PMIC_BKLIT_DISABLE:
pmic_bklit_master_disable();
break;
case
PMIC_SET_BKLIT:
if
((bklit_setting = kmalloc(sizeof(t_bklit_setting_param),
GFP_KERNEL)) == NULL) {
return
-ENOMEM;
}
if
(copy_from_user(bklit_setting, (t_bklit_setting_param *) arg,
sizeof(t_bklit_setting_param))) {
kfree(bklit_setting);
return
-EFAULT;
}
CHECK_ERROR_KFREE(pmic_bklit_set_mode(bklit_setting->channel,
bklit_setting->mode),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_current(bklit_setting->channel,
bklit_setting->
current_level),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_dutycycle
(bklit_setting->channel,
bklit_setting->duty_cycle),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_cycle_time
(bklit_setting->cycle_time),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_boost_mode
(bklit_setting->en_dis),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_config_boost_mode
(bklit_setting->abms,
bklit_setting->abr),
(kfree(bklit_setting)));
if
(bklit_setting->edge_slow != false) {
CHECK_ERROR_KFREE(pmic_bklit_enable_edge_slow(),
(kfree(bklit_setting)));
}
else {
CHECK_ERROR_KFREE(pmic_bklit_disable_edge_slow(),
(kfree(bklit_setting)));
}
kfree(bklit_setting);
break;
case
PMIC_GET_BKLIT:
if
((bklit_setting = kmalloc(sizeof(t_bklit_setting_param),
GFP_KERNEL)) == NULL) {
return
-ENOMEM;
}
if
(copy_from_user(bklit_setting, (t_bklit_setting_param *) arg,
sizeof(t_bklit_setting_param))) {
kfree(bklit_setting);
return
-EFAULT;
}
CHECK_ERROR_KFREE(pmic_bklit_get_current(bklit_setting->channel,
&bklit_setting->
current_level),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_get_cycle_time
(&bklit_setting->cycle_time),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_get_dutycycle
(bklit_setting->channel,
&bklit_setting->duty_cycle),
(kfree(bklit_setting)));
bklit_setting->strobe
= BACKLIGHT_STROBE_NONE;
CHECK_ERROR_KFREE(pmic_bklit_get_mode(bklit_setting->channel,
&bklit_setting->mode),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_get_edge_slow
(&bklit_setting->edge_slow),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_get_boost_mode
(&bklit_setting->en_dis),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_gets_boost_mode
(&bklit_setting->abms,
&bklit_setting->abr),
(kfree(bklit_setting)));
if
(copy_to_user((t_bklit_setting_param *) arg, bklit_setting,
sizeof(t_bklit_setting_param))) {
kfree(bklit_setting);
return
-EFAULT;
}
kfree(bklit_setting);
break;
case
PMIC_RAMPUP_BKLIT:
CHECK_ERROR(pmic_bklit_rampup((t_bklit_channel)
arg));
break;
case
PMIC_RAMPDOWN_BKLIT:
CHECK_ERROR(pmic_bklit_rampdown((t_bklit_channel)
arg));
break;
case
PMIC_OFF_RAMPUP_BKLIT:
CHECK_ERROR(pmic_bklit_off_rampup((t_bklit_channel)
arg));
break;
case
PMIC_OFF_RAMPDOWN_BKLIT:
CHECK_ERROR(pmic_bklit_off_rampdown((t_bklit_channel)
arg));
break;
case
STROBE_SLOW:
CHECK_ERROR_KFREE(pmic_tcled_fun_strobe
(fun_param->bank, fun_param->channel,
TC_STROBE_SLOW), (kfree(fun_param)));
break;
case
STROBE_FAST:
CHECK_ERROR_KFREE(pmic_tcled_fun_strobe
(fun_param->bank,
fun_param->channel, TC_STROBE_SLOW),
(kfree(fun_param)));
break;
case
CHASING_LIGHT_RGB_SLOW:
CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
(fun_param->bank, PMIC_RGB, TC_SLOW),
(kfree(fun_param)));
break;
…………………..
………………….
default:
return
-EINVAL;
}
return
0;
}
这是标准的设备驱动函数,为了文章的完整性,这里重述一下上篇文章的关于ioctl的解释。用户的ioctl函数原型为int ioctl(int fd, ind cmd, …),适合对设备的一些特性进行控制,其中fd就是用户程序打开设备时使用open函数返回的文件描述符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。当需要…这个参数时,把该参数的地址传递给static int pmic_battery_ioctl(struct inode *inode, struct file
*file, unsigned int cmd, unsigned long arg)的arg,以便进行数据的传入和回传(感觉是这样)。所以应用层只用调用标准的函数ioctl(int fd, ind cmd, …)并传递正确的cmd参数就可以了。这里就又有一个重要的事,就是分析以上代码,找出和背光有关的正确的cmd参数。
查看代码有两个case,查看后如下:
case
PMIC_BKLIT_ENABLE:
pmic_bklit_master_enable();////return
PMIC_NOT_SUPPORTED;
break;
case
PMIC_BKLIT_DISABLE:
pmic_bklit_master_disable();////return
PMIC_NOT_SUPPORTED;
break;
以上俩个函数内容均为return
PMIC_NOT_SUPPORTED;从函数内部的return可以看出这俩个命令无效,13783及驱动不支持以上两种操作。
关于背光的还有一个很重要的cmd,就是设置背光,如下:
case
PMIC_SET_BKLIT:
if
((bklit_setting = kmalloc(sizeof(t_bklit_setting_param),
GFP_KERNEL)) == NULL) {
return
-ENOMEM;
}
if
(copy_from_user(bklit_setting, (t_bklit_setting_param *) arg,
sizeof(t_bklit_setting_param))) {
kfree(bklit_setting);
return
-EFAULT;
}
//////以上为读入参数值出现错误返回,若成功后依次执行下边的函数进行模式修改,。
CHECK_ERROR_KFREE(pmic_bklit_set_mode(bklit_setting->channel,
bklit_setting->mode),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_current(bklit_setting->channel,
bklit_setting->current_level),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_dutycycle
(bklit_setting->channel,
bklit_setting->duty_cycle),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_cycle_time
(bklit_setting->cycle_time),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_set_boost_mode
(bklit_setting->en_dis),
(kfree(bklit_setting)));
CHECK_ERROR_KFREE(pmic_bklit_config_boost_mode
(bklit_setting->abms,
bklit_setting->abr),
(kfree(bklit_setting)));
if
(bklit_setting->edge_slow != false) {
CHECK_ERROR_KFREE(pmic_bklit_enable_edge_slow(),
(kfree(bklit_setting)));
}
else {
CHECK_ERROR_KFREE(pmic_bklit_disable_edge_slow(),
(kfree(bklit_setting)));
}
kfree(bklit_setting);
break;
后边的部分涉及到一个宏定义,在pmic_config.h中进行的定义,如下:
#define CHECK_ERROR_KFREE(func, freeptrs) /
do { /
int
ret = (func); /
if
(ret != PMIC_SUCCESS) /
////////////如果不成功
{ /
freeptrs; /
/////执行free也就是kfree(bklit_setting);
return ret; /
///////并且直接返回.
}/
} while(0);
可以看出该宏先执行func函数,成功则该宏完成,不成功则kfree(bklit_setting)并返回。可知一连串的CHECK_ERROR_KFREE宏定义实为依次执行各自的func,直到出现错误并立即返回。这里要求要提前把参数t_bklit_setting_param写好,然后通过命令写入寄存器.
关于t_bklit_setting_param,arch-mxc/pmic_light.h有这个定义,如下:
typedef struct {
t_bklit_channel
channel; /*!< Channel */
t_bklit_mode
mode; /*!< Mode */
t_bklit_strobe_mode
strobe; /*!< Strobe mode */
unsigned
char current_level; /*!< Current level
*/
unsigned
char duty_cycle; /*!< Duty cycle */
unsigned
char cycle_time; /*!< Cycle time */
bool
edge_slow; /*!< Edge Slow */
bool
en_dis; /*!< Enable disable
boost mode */
unsigned
int abms; /*!< Adaptive boost
* mode
selection */
unsigned
int abr; /*!< Adaptive
*
boost reference */
} t_bklit_setting_param;
在case PMIC_SET_BKLIT:中调用了很多函数,比如pmic_bklit_set_mode,该文件中有定义:
PMIC_STATUS
pmic_bklit_set_mode(t_bklit_channel channel, t_bklit_mode mode)
{
unsigned
int reg_value = 0;
unsigned
int clear_val = 0;
unsigned
int triode_val = 0;
if
(suspend_flag == 1) {
return
-EBUSY;
}
CHECK_ERROR(pmic_read_reg(LREG_0,
®_value, PMIC_ALL_BITS));
switch
(channel) {
case
BACKLIGHT_LED1:
clear_val
= ~(MASK_TRIODE_MAIN_BL);
triode_val
= MASK_TRIODE_MAIN_BL;
break;
case
BACKLIGHT_LED2:
clear_val
= ~(MASK_TRIODE_MAIN_BL << INDEX_AUXILIARY);
triode_val
= (MASK_TRIODE_MAIN_BL << INDEX_AUXILIARY);
break;
case
BACKLIGHT_LED3:
clear_val
= ~(MASK_TRIODE_MAIN_BL << INDEX_KEYPAD);
triode_val
= (MASK_TRIODE_MAIN_BL << INDEX_KEYPAD);
break;
default:
return
PMIC_PARAMETER_ERROR;
}
reg_value
= (reg_value & clear_val);
if
(mode == BACKLIGHT_TRIODE_MODE) {
reg_value
= (reg_value | triode_val);
}
CHECK_ERROR(pmic_write_reg(LREG_0,
reg_value, PMIC_ALL_BITS));
return
PMIC_SUCCESS;
}
分析上边这个函数可以知道t_bklit_channel channel这个参数很重要,直接决定了修改的寄存器的bit位置,t_bklit_channel包含在t_bklit_setting_param参数中,这个参数很重要,但是搜索后没有发现他的初始化内容。
这里很惶惑如何设置这些参数,但又发现可以把初始化好的参数读出来,然后修改后写回就可以了。如下case:
case PMIC_GET_BKLIT:
……………………
很幸运,在上边的case中发现了可以读出设置的cmd,这样的话可以先读,修改后再写入。
以上方法虽然可行但并不是一个很好的办法,后来发现,
/////pmic_testapp_light.c中的一个函数,
void enable_backlight(int fp,
t_bklit_setting_param * setting)
{
setting->channel
= BACKLIGHT_LED1;
setting->current_level
= 4;
setting->duty_cycle
= 7;
setting->mode
= BACKLIGHT_CURRENT_CTRL_MODE;
setting->cycle_time
= 2;
setting->strobe
= BACKLIGHT_STROBE_NONE;
setting->edge_slow
= false;
setting->en_dis
= 0;
setting->abms
= 0;
setting->abr
= 0;
printf("Main
Backlight LED ON./n");
ioctl(fp,
PMIC_SET_BKLIT, setting);
sleep(1);
setting->channel
= BACKLIGHT_LED2;
printf("Auxiliary
Backlight LED ON./n");
ioctl(fp,
PMIC_SET_BKLIT, setting);
sleep(1);
printf("Keypad
Backlight LED ON./n");
setting->channel
= BACKLIGHT_LED3;
ioctl(fp,
PMIC_SET_BKLIT, setting);
/*
Check result */
if(ask_user("Did
you see the BACKLIGHTS ON?") == TPASS)
printf("Test
Passed/n");
else
printf("Test
Failed/n");
}
这样可以确定, setting->channel
= BACKLIGHT_LED1对应Main
Backlight LED
setting->channel = BACKLIGHT_LED2对应Auxiliary Backlight LED
setting->channel = BACKLIGHT_LED3对应Keypad Backlight LED
lcd的背光应该是Main Backlight LED,这样就可以仿照上边的初始化确定设置了。
四.参数PMIC_SET_BKLIT等参数的定义
通过查看代码,在 <asm/arch/pmic_light.h>中有如下定义:
#define PMIC_SET_BKLIT
_IOW('p', 0xe2, int)
#define PMIC_GET_BKLIT _IOWR('p', 0xe3, int)
………
而在/include/asm-generic/ioctl.h中有如下定义:
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
#define _IOC_NONE 0U
#define _