想在应用层自己可调整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 _IOC_WRITE 1U
#define _IOC_READ 2U
#define _IOC_TYPECHECK(t) /
((sizeof(t) == sizeof(t[1]) && /
sizeof(t) < (1 << _IOC_SIZEBITS)) ? /
sizeof(t) : __invalid_size_argument_for_IOC)
#define _IOC(dir,type,nr,size) /
(((dir) << _IOC_DIRSHIFT) | /
((type) << _IOC_TYPESHIFT) | /
((nr) << _IOC_NRSHIFT) | /
((size) << _IOC_SIZESHIFT))
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
以上宏定义完成了对cmd参数PMIC_SET_BKLIT的定义。BACKLIGHT_LED1、BACKLIGHT_CURRENT_CTRL_MODE、BACKLIGHT_STROBE_NONE等参数在该头文件中也都有定义,可以进行查看,不再详述,会在下边的应用程序中用定义的方法列出。
五.编写程序设置背光
编写一函数light-set.c如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/wait.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)
/*
* Direction bits.
*/
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
#define _IOC_READ 2U
#define _IOC(dir,type,nr,size) /
(((dir) << _IOC_DIRSHIFT) | /
((type) << _IOC_TYPESHIFT) | /
((nr) << _IOC_NRSHIFT) | /
((size) << _IOC_SIZESHIFT))
#define _IOC_TYPECHECK(t) /
((sizeof(t) == sizeof(t[1]) && /
sizeof(t) < (1 << _IOC_SIZEBITS)) ? /
sizeof(t) : __invalid_size_argument_for_IOC)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define PMIC_SET_BKLIT _IOW('p', 0xe2, int)
#define false 0
#define true 1
typedef enum {
BACKLIGHT_LED1, /*! < Backlight channel 1 */
BACKLIGHT_LED2, /*! < Backlight channel 2 */
BACKLIGHT_LED3 /*! < Backlight channel 3 */
} t_bklit_channel;
typedef enum {
BACKLIGHT_CURRENT_CTRL_MODE, /*! < Current control mode */
BACKLIGHT_TRIODE_MODE /*! < Triode mode */
} t_bklit_mode;
typedef enum {
/*!
* No Strobe Light Pulsing
*/
BACKLIGHT_STROBE_NONE,
BACKLIGHT_STROBE_FAST,
BACKLIGHT_STROBE_SLOW
} t_bklit_strobe_mode;
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 */
int edge_slow; /*!< Edge Slow */ //bool 报错,改为int,
int en_dis; /*!< Enable disable boost mode */ //bool 报错,改为int,
unsigned int abms; /*!< Adaptive boost
* mode selection */
unsigned int abr; /*!< Adaptive
* boost reference */
} t_bklit_setting_param;
main()
{
int fd,status;
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;
/////setting.duty_cycle = 1~15;改变了背光的占空比。
fd = open("/dev/pmic_light", O_RDWR);
if (fd < 0) {
printf("/dev/pmic_light open error!!");
return -1;
}
status =ioctl(fd, PMIC_SET_BKLIT, &setting);
if (status < 0) {
printf("/dev/pmic_light set error!!");
return -1;
}
printf("/dev/pmic_light cycle is %d!!",setting.duty_cycle);
return 1;
}
上边这个应用程序中setting.duty_cycle是关于背光的占空比设置的,可设置为1~15,其中15为百分之百的占空比。数值越小,占空比越小,背光越暗。这样我们通过改变setting.duty_cycle的值,就可以随心所欲的进行背光亮度的调节了。