ALSPS光线距离传感器驱动分析
一.alsps的初始化函数和重要结构体
epl2182_init //
Epl2182.c (kernel-3.10\drivers\misc\mediatek\alsps\epl2182-new)
struct alsps_hw *hw = get_cust_alsps_hw(); //得到配置和硬件信息
i2c_register_board_info(hw->i2c_num, &i2c_EPL2182, 1); //注册I2C信息
list_add_tail(&devinfo->list, &__i2c_board_list); //加入到__i2c_board_list链表中
alsps_driver_add(&epl2182_init_info); // alsps_local_init函数加入
alsps_init_list[i] = obj; //加入到alsps_init_list[i] 数组中,以备后面用
alsps_local_init //这个函数在alsps核心调用,下面有分析
get_cust_alsps_hw(); //得到配置和硬件信息
epl2182_power(hw, 1); //上电
//mtk专门的上电函数(pmic.c),很多模块都在用,if(hw->power_id != POWER_NONE_MACRO) 会进行上电操作,这里只是一个赋值操作power_on = on;
hwPowerOn(hw->power_id, hw->power_vol, "EPL2182")
i2c_add_driver(&epl2182_i2c_driver) //I2C注册driver
i2c_driver 结构体:
static struct i2c_driver epl2182_i2c_driver =
{
.probe = epl2182_i2c_probe,
.remove = epl2182_i2c_remove,
.detect = epl2182_i2c_detect,
.suspend = epl2182_i2c_suspend,
.resume = epl2182_i2c_resume,
.id_table = epl2182_i2c_id,
.driver = {
.name = EPL2182_DEV_NAME,
},
};
二.I2C的alsps的probe函数
通过i2c_driver 与adapter的匹配调用epl2182_i2c_probe
epl2182_i2c_probe
epl2182_get_addr(obj->hw, &obj->addr); //得到I2C的地址
INIT_WORK(&obj->eint_work, epl2182_eint_work); //初始化中断工作任务
epld = g_epl2182_ptr; //得到epl2182私有数据
elan_epl2182_I2C_Read(epld->client,REG_16,R_TWO_BYTE,0x02,read_data); //i2c的读函数,读channel1,ps的数据
elan_epl2182_I2C_Read(epld->client,REG_13,R_SINGLE_BYTE,0x01,read_data); //读取到PS的中断状态,0或者1
ps_report_interrupt_data
ps_data_report(cxt->idev,value,3);
input_report_rel(dev, EVENT_TYPE_PS_VALUE, (value+1)); //输入子系统上报数据
input_report_rel(dev, EVENT_TYPE_PS_STATUS, status);
input_sync(dev);
INIT_WORK(&obj->data_work, epl2182_check_ps_data); //也是上报数据
elan_epl2182_I2C_Write(client,REG_0,W_SINGLE_BYTE,0x02, EPL_S_SENSING_MODE); //把光照转换到电压的时间
elan_epl2182_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_DISABLE); //把中断清零
epl2182_init_client
if(obj->hw->polling_mode_ps == 0) //这里是中断模式
epl2182_setup_eint(client) //
g_epl2182_ptr = obj; //后面有用到epl2182的属性
hw8k_init_device
epl2182_i2c_client=client; //后面有用到
misc_register(&epl2182_device) //注册混杂设备
als_ctl.open_report_data= als_open_report_data; //als_control的赋值
als_ctl.enable_nodata = als_enable_nodata;
als_ctl.set_delay = als_set_delay;
混杂设备结构体:
static struct miscdevice epl2182_device =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "als_ps",
.fops = &epl2182_fops,
};
static struct file_operations epl2182_fops =
{
.owner = THIS_MODULE,
.open = epl2182_open,
.release = epl2182_release,
.unlocked_ioctl = epl2182_unlocked_ioctl,
};
三.alsps核心
在alsps.c中是alsp的上层部分,在epl2182_init之后调用
static struct platform_driver alsps_driver =
{
.probe = alsps_probe,
.remove = alsps_remove,
.suspend = alsps_suspend,
.resume = alsps_resume,
.driver =
{
.name = ALSPS_PL_DEV_NAME,
#ifdef CONFIG_OF
.of_match_table = alsps_of_match,
#endif
}
}; //平台driver结构体
alsps_init
platform_driver_register(&alsps_driver) //注册平台驱动,在mt_devs.c中有平台device:platform_device alsps_sensor
alsps_probe //匹配后调用它
alsps_context_alloc_object
alsps_context *obj = kzalloc(sizeof(*obj), GFP_KERNEL); //分配alsps_context 结构体,保存了很多属性
初始化obj结构体,比如work的初始化:als_work_func,ps_work_func;定时器初始化,执行函数:als_poll,ps_poll分别执行schedule_work(&obj->report_als); schedule_work(&obj->report_ps);
alsps_real_driver_init //初始化实际的alsps
alsps_real_driver_init //
alsps_init_list[i]->init(); //这就是具体alsps驱动注册的init函数,在这里调用了
alsps_factory_device_init //is_use_common_factory被设置为true,就会进行misc_register(&alsps_factory_device),这里不用。
alsps_input_init //输入子系统的初始化,注册
input_allocate_device
input_register_device
四.应用读取sensor数据:
als_store_active 和 ps_store_active启动sensor,然后上报数据,PS一般用中断,als一般用轮询!
五.Light sensor level和value的设置
[DESCRIPTION]Light sensor level和value的设置
[PLATFORM]
MT6575 MT6577 MT6589
[SOLUTION]
Als_level是从sensor读到的raw data, als_value是由als_level转换为上报android的值,以
ltr559为例:在cust_alsps.c中 :
该值要客制化,请联系sensor vender提供。
由于结构限制或者传感器本身原因,工厂模式下检测到ic出来的值变化不够明显,可检测的范围小等。导致自动背光效果不明显。
[SOLUTION]
硬件上没有方法改动的话,建议合理的修改,cust_alsps.c中的.level和.value这2个数组的值,driver里处理数据的原理:
从driver里传感器IC寄存器出来的数据是level值,根据此level值参照上述2个数组的对应关系,由level数组的下标取到.value数组的下标对应的值,然后上报value值给工厂模式和上层看到。
所以需要您调cust_alsps.c里的那2个数组,如果是被遮挡而引起变化范围变小的话,应该是寄存器出来的level变小了,所以建议把cust-als.c里的level也相应的减小,value不变,则应该有所改善。多试几组减小.level的情况。
另外如果需要更细腻的背光等级,可以适当的把这2个数组加大,取值更连续一些。(这个映射还与android的value(LUX)--->255级背光的映射有关,也不是连续的)
请参考 frameworks\base\core\res\res\values\config.xml
六.兼容
当遇到两个sensor同一个i2c地址,只启用一个的时候,可以在注册的时候该一个地址,然后在probe中改回来就可以了。
通过工程模式可以看到sensor的raw 打他,通过工厂测试模式可以看到所有senso人上报的数值。
七.工程测试模式中sensor的读取数据流程
1.输入*987*0#进行测试,一般用于产线的测试
epl2182_unlocked_ioctl : 用的是poll(ps)
2.*#*#3646633#*#*这样是可以读到sensor的原始数据,可以调试sensor是否有遮挡
epl2182_unlocked_ioctl 用的是poll
八.把ps改成轮询方式
现在的机制是:
1. PS单独开启的时候用中断;
2.单独ALS是轮询
3.PS与ALS一起开启的时候在als_get_data中轮询ALS,如果是中断模式还要轮询PS,因为轮询als的时候ps不会产生中断
把PS也改下轮询:
因为poll的时候会互相干扰,产生竞争,导致数据闪动,需要信号量来控制
als_get_data
mutex_lock(&epl2182_poll_mutex);...............mutex_unlock(&epl2182_poll_mutex);
ps_get_data
mutex_lock(&epl2182_poll_mutex);
...............
mutex_unlock(&epl2182_poll_mutex);
PS在打电话的时候不能休眠:加睡眠锁
目前的driver架构是对于没有使用中断模式的距离传感器(cust_alsps.c .ps_polling=1)在驱动的i2c_probe里初始化一个wake lock,在Operate函数的ENABLE分支,如果是
enable的话就wake_lock(your_lock),disable的话就wake_unlock(your_lock)