mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-10 23:18:10 +09:00
835 lines
22 KiB
C
Executable File
835 lines
22 KiB
C
Executable File
/* drivers/input/misc/al3006.c
|
|
*
|
|
* Copyright (C) 2010 ROCK-CHIPS, Inc.
|
|
* Author: eric <hc@rock-chips.com>
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/input.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/circ_buf.h>
|
|
#include <linux/interrupt.h>
|
|
#include "al3006.h"
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
#include <linux/earlysuspend.h>
|
|
#endif
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/wait.h>
|
|
|
|
#define AL3006_DBG 0
|
|
|
|
#if AL3006_DBG
|
|
#define AL3006_DEBUG(x...) printk(x)
|
|
#else
|
|
#define AL3006_DEBUG(x...)
|
|
#endif
|
|
|
|
#define CONFIG_REG (0x00)
|
|
#define TIM_CTL_REG (0x01)
|
|
#define ALS_CTL_REG (0x02)
|
|
#define INT_STATUS_REG (0x03)
|
|
#define PS_CTL_REG (0x04)
|
|
#define PS_ALS_DATA_REG (0x05)
|
|
#define ALS_WINDOWS_REG (0x08)
|
|
|
|
//enable bit[ 0-1], in register CONFIG_REG
|
|
#define ONLY_ALS_EN (0x00)
|
|
#define ONLY_PROX_EN (0x01)
|
|
#define ALL_PROX_ALS_EN (0x02)
|
|
#define ALL_IDLE (0x03)
|
|
|
|
#define POWER_MODE_MASK (0x0C)
|
|
#define POWER_UP_MODE (0x00)
|
|
#define POWER_DOWN_MODE (0x08)
|
|
#define POWER_RESET_MODE (0x0C)
|
|
|
|
struct al3006_data {
|
|
struct input_dev *psensor_input_dev;
|
|
struct input_dev *lsensor_input_dev;
|
|
struct i2c_client *client;
|
|
struct delayed_work dwork; //for l/psensor
|
|
//struct delayed_work l_work; //for light sensor
|
|
struct mutex lock;
|
|
spinlock_t work_lock;
|
|
int enabled;
|
|
int irq;
|
|
};
|
|
static struct al3006_data al3006_struct_data;
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
static struct early_suspend al3006_early_suspend;
|
|
#endif
|
|
|
|
int g_lightlevel = 8;
|
|
|
|
static const int luxValues[8] = {
|
|
10, 160, 225, 320,
|
|
640, 1280, 2600, 4095
|
|
};
|
|
|
|
|
|
static int al3006_read_reg(struct i2c_client *client, char reg, char *value)
|
|
{
|
|
int ret = 0;
|
|
struct i2c_msg msg[2];
|
|
struct i2c_adapter *adap = client->adapter;
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = client->flags;
|
|
msg[0].len = 1;
|
|
msg[0].buf = (char *)®
|
|
msg[0].scl_rate = 400 * 1000;
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = client->flags | I2C_M_RD;
|
|
msg[1].len = 1;
|
|
msg[1].buf = (char *)value;
|
|
msg[1].scl_rate = 400 * 1000;
|
|
|
|
if ((ret = i2c_transfer(adap, (struct i2c_msg *)&msg, 2)) < 2) {
|
|
AL3006_DEBUG("%s: read al3006 register %#x failure\n", __FUNCTION__, reg);
|
|
return -EIO;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int al3006_write_reg(struct i2c_client *client, char reg, char value)
|
|
{
|
|
int ret = 0;
|
|
char buf[2];
|
|
struct i2c_msg msg;
|
|
struct i2c_adapter *adap = client->adapter;
|
|
|
|
buf[0] = reg;
|
|
buf[1] = value;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = client->flags;
|
|
msg.len = 2;
|
|
msg.buf = (char *)&buf;
|
|
msg.scl_rate = 400 * 1000;
|
|
|
|
|
|
if ((ret = i2c_transfer(adap, (struct i2c_msg *)&msg, 1)) < 1) {
|
|
AL3006_DEBUG("%s: read al3006 register %#x failure\n", __FUNCTION__, reg);
|
|
return -EIO;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void al3006_change_ps_threshold(struct i2c_client *client)
|
|
{
|
|
struct al3006_data *al3006 = i2c_get_clientdata(client);
|
|
char reg, value;
|
|
|
|
AL3006_DEBUG("%s:\n", __FUNCTION__);
|
|
mutex_lock(&al3006->lock);
|
|
reg = PS_ALS_DATA_REG;
|
|
al3006_read_reg(client, reg, &value);
|
|
mutex_unlock(&al3006->lock);
|
|
|
|
value >>= 7; //bit7 is ps data ; bit7 = 1, object is detected
|
|
printk("%s: psensor's data is %#x\n", __FUNCTION__, value);
|
|
|
|
input_report_abs(al3006->psensor_input_dev, ABS_DISTANCE, value?0:1);
|
|
input_sync(al3006->psensor_input_dev);
|
|
}
|
|
|
|
static void al3006_change_ls_threshold(struct i2c_client *client)
|
|
{
|
|
struct al3006_data *al3006 = i2c_get_clientdata(client);
|
|
char reg, value;
|
|
|
|
AL3006_DEBUG("%s:\n", __FUNCTION__);
|
|
mutex_lock(&al3006->lock);
|
|
reg = PS_ALS_DATA_REG;
|
|
al3006_read_reg(client, reg, &value);
|
|
mutex_unlock(&al3006->lock);
|
|
|
|
value &= 0x3F; // bit0-5 is ls data;
|
|
printk("%s: lightsensor's level is %#x\n", __FUNCTION__, value);
|
|
|
|
if(value > 8) value = 8;
|
|
input_report_abs(al3006->lsensor_input_dev, ABS_MISC, value);
|
|
input_sync(al3006->lsensor_input_dev);
|
|
}
|
|
|
|
static void al3006_work_handler(struct work_struct *work)
|
|
{
|
|
struct al3006_data *al3006 = (struct al3006_data *)container_of(work, struct al3006_data, dwork.work);
|
|
char reg, value;
|
|
|
|
mutex_lock(&al3006->lock);
|
|
reg = INT_STATUS_REG;
|
|
al3006_read_reg(al3006->client, reg, &value);
|
|
mutex_unlock(&al3006->lock);
|
|
AL3006_DEBUG("%s: INT_STATUS_REG is %#x\n", __FUNCTION__, value);
|
|
|
|
value &= 0x03;
|
|
if(value == 0x02) { //ps int
|
|
al3006_change_ps_threshold(al3006->client);
|
|
}
|
|
else if(value == 0x01) { //ls int
|
|
al3006_change_ls_threshold(al3006->client);
|
|
}
|
|
else if(value == 0x03) { //ps and ls int
|
|
al3006_change_ps_threshold(al3006->client);
|
|
al3006_change_ls_threshold(al3006->client);
|
|
}
|
|
//enable_irq(al3006->irq);
|
|
}
|
|
|
|
static void al3006_reschedule_work(struct al3006_data *data,
|
|
unsigned long delay)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&data->work_lock, flags);
|
|
|
|
/*
|
|
* If work is already scheduled then subsequent schedules will not
|
|
* change the scheduled time that's why we have to cancel it first.
|
|
*/
|
|
__cancel_delayed_work(&data->dwork);
|
|
schedule_delayed_work(&data->dwork, delay);
|
|
|
|
spin_unlock_irqrestore(&data->work_lock, flags);
|
|
}
|
|
|
|
static irqreturn_t al3006_irq_handler(int irq, void *data)
|
|
{
|
|
struct al3006_data *al3006 = (struct al3006_data *)data;
|
|
AL3006_DEBUG("%s\n", __FUNCTION__);
|
|
//input_report_abs(al3006->psensor_input_dev, ABS_DISTANCE, 0);
|
|
//input_sync(al3006->psensor_input_dev);
|
|
|
|
//disable_irq_nosync(al3006->irq);
|
|
al3006_reschedule_work(al3006, 0);//msecs_to_jiffies(420)
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int al3006_psensor_enable(struct i2c_client *client)
|
|
{
|
|
char reg, value;
|
|
int ret;
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
|
|
AL3006_DEBUG("%s:\n", __FUNCTION__);
|
|
mutex_lock(&al3006->lock);
|
|
reg = CONFIG_REG;
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
if( (value & 0x03) == ONLY_ALS_EN ){
|
|
value &= ~0x03;
|
|
value |= ALL_PROX_ALS_EN;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
else if( (value & 0x03) == ALL_IDLE ){
|
|
value &= ~0x03;
|
|
value |= ONLY_PROX_EN;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
#ifdef AL3006_DBG
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
AL3006_DEBUG("%s: configure reg value %#x ...\n", __FUNCTION__, value);
|
|
#endif
|
|
|
|
reg = PS_ALS_DATA_REG;
|
|
al3006_read_reg(client, reg, &value);
|
|
|
|
value >>= 7; //bit7 is ps data ; bit7 = 1, object is detected
|
|
printk("%s: psensor's data is %#x\n", __FUNCTION__, value);
|
|
|
|
input_report_abs(al3006->psensor_input_dev, ABS_DISTANCE, value?0:1);
|
|
input_sync(al3006->psensor_input_dev);
|
|
mutex_unlock(&al3006->lock);
|
|
|
|
//enable_irq(al3006->irq);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int al3006_psensor_disable(struct i2c_client *client)
|
|
{
|
|
char ret, reg, value;
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
|
|
mutex_lock(&al3006->lock);
|
|
reg = CONFIG_REG;
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
if( (value & 0x03) == ONLY_PROX_EN ){
|
|
value &= ~0x03;
|
|
value |= ALL_IDLE;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
else if( (value & 0x03) == ALL_PROX_ALS_EN ){
|
|
value &= ~0x03;
|
|
value |= ONLY_ALS_EN;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
#ifdef AL3006_DBG
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
AL3006_DEBUG("%s: configure reg value %#x ...\n", __FUNCTION__, value);
|
|
#endif
|
|
mutex_unlock(&al3006->lock);
|
|
|
|
//disable_irq(al3006->irq);
|
|
//cancel_delayed_work_sync(&al3006->dwork);
|
|
//enable_irq(al3006->irq);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int misc_ps_opened = 0;
|
|
|
|
static int al3006_psensor_open(struct inode *inode, struct file *file)
|
|
{
|
|
// struct i2c_client *client =
|
|
// container_of (al3006_psensor_misc.parent, struct i2c_client, dev);
|
|
printk("%s\n", __func__);
|
|
if (misc_ps_opened)
|
|
return -EBUSY;
|
|
misc_ps_opened = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int al3006_psensor_release(struct inode *inode, struct file *file)
|
|
{
|
|
// struct i2c_client *client =
|
|
// container_of (al3006_psensor_misc.parent, struct i2c_client, dev);
|
|
printk("%s\n", __func__);
|
|
misc_ps_opened = 0;
|
|
return 0;
|
|
}
|
|
|
|
static long al3006_psensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
char reg, val, enabled;
|
|
struct al3006_data *al3006 = &al3006_struct_data;
|
|
struct i2c_client *client = al3006->client;
|
|
|
|
printk("%s cmd %d\n", __func__, _IOC_NR(cmd));
|
|
switch (cmd) {
|
|
case PSENSOR_IOCTL_ENABLE:
|
|
if (get_user(val, (unsigned long __user *)arg))
|
|
return -EFAULT;
|
|
if (val)
|
|
return al3006_psensor_enable(client);
|
|
else
|
|
return al3006_psensor_disable(client);
|
|
break;
|
|
case PSENSOR_IOCTL_GET_ENABLED:
|
|
mutex_lock(&al3006->lock);
|
|
reg = CONFIG_REG;
|
|
al3006_read_reg(client, reg, &val);
|
|
mutex_unlock(&al3006->lock);
|
|
val &= 0x03;
|
|
if(val == ONLY_PROX_EN || val == ALL_PROX_ALS_EN)
|
|
enabled = 1;
|
|
else
|
|
enabled = 0;
|
|
return put_user(enabled, (unsigned long __user *)arg);
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static struct file_operations al3006_psensor_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = al3006_psensor_open,
|
|
.release = al3006_psensor_release,
|
|
.unlocked_ioctl = al3006_psensor_ioctl
|
|
};
|
|
|
|
static struct miscdevice al3006_psensor_misc = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "psensor",
|
|
.fops = &al3006_psensor_fops
|
|
};
|
|
|
|
static int register_psensor_device(struct i2c_client *client, struct al3006_data *data)
|
|
{
|
|
struct input_dev *input_dev = data->psensor_input_dev;
|
|
int rc;
|
|
|
|
AL3006_DEBUG("%s: allocating input device psensor\n", __func__);
|
|
input_dev = input_allocate_device();
|
|
if (!input_dev) {
|
|
dev_err(&client->dev,"%s: could not allocate input device for psensor\n", __FUNCTION__);
|
|
rc = -ENOMEM;
|
|
goto done;
|
|
}
|
|
data->psensor_input_dev = input_dev;
|
|
input_set_drvdata(input_dev, data);
|
|
|
|
input_set_drvdata(input_dev, data);
|
|
input_dev->name = "proximity";
|
|
set_bit(EV_ABS, input_dev->evbit);
|
|
input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0);
|
|
|
|
AL3006_DEBUG("%s: registering input device psensor\n", __FUNCTION__);
|
|
rc = input_register_device(input_dev);
|
|
if (rc < 0) {
|
|
pr_err("%s: could not register input device for psensor\n", __FUNCTION__);
|
|
goto done;
|
|
}
|
|
|
|
AL3006_DEBUG("%s: registering misc device for psensor\n", __FUNCTION__);
|
|
rc = misc_register(&al3006_psensor_misc);
|
|
if (rc < 0) {
|
|
pr_err("%s: could not register misc device psensor\n", __FUNCTION__);
|
|
goto err_unregister_input_device;
|
|
}
|
|
al3006_psensor_misc.parent = &client->dev;
|
|
|
|
//INIT_DELAYED_WORK(&data->p_work, al3006_psensor_work_handler);
|
|
|
|
return 0;
|
|
|
|
err_unregister_input_device:
|
|
input_unregister_device(input_dev);
|
|
done:
|
|
return rc;
|
|
}
|
|
|
|
static void unregister_psensor_device(struct i2c_client *client, struct al3006_data *data)
|
|
{
|
|
misc_deregister(&al3006_psensor_misc);
|
|
input_unregister_device(data->psensor_input_dev);
|
|
}
|
|
|
|
#define LSENSOR_POLL_PROMESHUTOK 1000
|
|
|
|
static int al3006_lsensor_enable(struct i2c_client *client)
|
|
{
|
|
char reg, value;
|
|
int ret;
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
|
|
mutex_lock(&al3006->lock);
|
|
|
|
reg = CONFIG_REG;
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
if( (value & 0x03) == ONLY_PROX_EN ){
|
|
value &= ~0x03;
|
|
value |= ALL_PROX_ALS_EN;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
else if( (value & 0x03) == ALL_IDLE ){
|
|
value &= ~0x03;
|
|
value |= ONLY_ALS_EN;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
#ifdef AL3006_DBG
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
AL3006_DEBUG("%s: configure reg value %#x ...\n", __FUNCTION__, value);
|
|
#endif
|
|
|
|
mutex_unlock(&al3006->lock);
|
|
|
|
//schedule_delayed_work(&(al3006->l_work), msecs_to_jiffies(LSENSOR_POLL_PROMESHUTOK));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int al3006_lsensor_disable(struct i2c_client *client)
|
|
{
|
|
char ret, reg, value;
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
|
|
//cancel_delayed_work_sync(&(al3006->l_work));
|
|
|
|
mutex_lock(&al3006->lock);
|
|
reg = CONFIG_REG;
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
if( (value & 0x03) == ONLY_ALS_EN ){
|
|
value &= ~0x03;
|
|
value |= ALL_IDLE;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
else if( (value & 0x03) == ALL_PROX_ALS_EN ){
|
|
value &= ~0x03;
|
|
value |= ONLY_PROX_EN;
|
|
ret = al3006_write_reg(client, reg, value);
|
|
}
|
|
#ifdef AL3006_DBG
|
|
ret = al3006_read_reg(client, reg, &value);
|
|
AL3006_DEBUG("%s: configure reg value %#x ...\n", __FUNCTION__, value);
|
|
#endif
|
|
mutex_unlock(&al3006->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int luxValue_to_level(int value)
|
|
{
|
|
int i;
|
|
if (value >= luxValues[7])
|
|
return 7;
|
|
if (value <= luxValues[0])
|
|
return 0;
|
|
for (i=0;i<7;i++)
|
|
if (value>=luxValues[i] && value<luxValues[i+1])
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
static int misc_ls_opened = 0;
|
|
|
|
static int al3006_lsensor_open(struct inode *inode, struct file *file)
|
|
{
|
|
// struct i2c_client *client =
|
|
// container_of (al3006_lsensor_misc.parent, struct i2c_client, dev);
|
|
printk("%s\n", __func__);
|
|
if (misc_ls_opened)
|
|
return -EBUSY;
|
|
misc_ls_opened = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int al3006_lsensor_release(struct inode *inode, struct file *file)
|
|
{
|
|
|
|
// struct i2c_client *client =
|
|
// container_of (al3006_lsensor_misc.parent, struct i2c_client, dev);
|
|
printk("%s\n", __func__);
|
|
misc_ls_opened = 0;
|
|
return 0;
|
|
}
|
|
|
|
static long al3006_lsensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
char reg, val, enabled;
|
|
struct al3006_data *al3006 = &al3006_struct_data;
|
|
struct i2c_client *client = al3006->client;
|
|
|
|
printk("%s cmd %d\n", __FUNCTION__, _IOC_NR(cmd));
|
|
switch (cmd) {
|
|
case LIGHTSENSOR_IOCTL_ENABLE:
|
|
if (get_user(val, (unsigned long __user *)arg))
|
|
return -EFAULT;
|
|
if (val)
|
|
return al3006_lsensor_enable(client);
|
|
else
|
|
return al3006_lsensor_disable(client);
|
|
break;
|
|
case LIGHTSENSOR_IOCTL_GET_ENABLED:
|
|
mutex_lock(&al3006->lock);
|
|
reg =CONFIG_REG;
|
|
al3006_read_reg(client, reg, &val);
|
|
mutex_unlock(&al3006->lock);
|
|
val &= 0x03;
|
|
if(val == ONLY_ALS_EN || val == ALL_PROX_ALS_EN)
|
|
enabled = 1;
|
|
else
|
|
enabled = 0;
|
|
return put_user(enabled, (unsigned long __user *)arg);
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static struct file_operations al3006_lsensor_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = al3006_lsensor_open,
|
|
.release = al3006_lsensor_release,
|
|
.unlocked_ioctl = al3006_lsensor_ioctl
|
|
};
|
|
|
|
static struct miscdevice al3006_lsensor_misc = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "lightsensor",
|
|
.fops = &al3006_lsensor_fops
|
|
};
|
|
|
|
static int register_lsensor_device(struct i2c_client *client, struct al3006_data *data)
|
|
{
|
|
struct input_dev *input_dev = data->lsensor_input_dev;
|
|
int rc;
|
|
|
|
AL3006_DEBUG("%s: allocating input device lsensor\n", __func__);
|
|
input_dev = input_allocate_device();
|
|
if (!input_dev) {
|
|
dev_err(&client->dev,"%s: could not allocate input device for lsensor\n", __FUNCTION__);
|
|
rc = -ENOMEM;
|
|
goto done;
|
|
}
|
|
data->lsensor_input_dev = input_dev;
|
|
input_set_drvdata(input_dev, data);
|
|
|
|
input_set_drvdata(input_dev, data);
|
|
input_dev->name = "lightsensor-level";
|
|
set_bit(EV_ABS, input_dev->evbit);
|
|
input_set_abs_params(input_dev, ABS_MISC, 0, 8, 0, 0);
|
|
|
|
AL3006_DEBUG("%s: registering input device al3006 lsensor\n", __FUNCTION__);
|
|
rc = input_register_device(input_dev);
|
|
if (rc < 0) {
|
|
pr_err("%s: could not register input device for lsensor\n", __FUNCTION__);
|
|
goto done;
|
|
}
|
|
|
|
AL3006_DEBUG("%s: registering misc device for al3006's lsensor\n", __FUNCTION__);
|
|
rc = misc_register(&al3006_lsensor_misc);
|
|
if (rc < 0) {
|
|
pr_err("%s: could not register misc device lsensor\n", __FUNCTION__);
|
|
goto err_unregister_input_device;
|
|
}
|
|
|
|
al3006_lsensor_misc.parent = &client->dev;
|
|
|
|
//INIT_DELAYED_WORK(&data->l_work, al3006_lsensor_work_handler);
|
|
|
|
return 0;
|
|
|
|
err_unregister_input_device:
|
|
input_unregister_device(input_dev);
|
|
done:
|
|
return rc;
|
|
}
|
|
|
|
static void unregister_lsensor_device(struct i2c_client *client, struct al3006_data *al3006)
|
|
{
|
|
misc_deregister(&al3006_lsensor_misc);
|
|
input_unregister_device(al3006->lsensor_input_dev);
|
|
}
|
|
|
|
static int al3006_config(struct i2c_client *client)
|
|
{
|
|
char value;
|
|
//struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
|
|
AL3006_DEBUG("%s: init al3006 all register\n", __FUNCTION__);
|
|
|
|
/***********************config**************************/
|
|
value = 0x41;//The ADC effective resolution = 9; Low lux threshold level = 1;
|
|
//value = 0x69; //The ADC effective resolution = 17; Low lux threshold level = 9;
|
|
al3006_write_reg(client, ALS_CTL_REG, value);
|
|
|
|
//value = 0x04;//0x01-0x0f; 17%->93.5% if value = 0x04,then Compensate Loss 52%
|
|
value = 0x02;//0x01-0x0f; 17%->93.5% if value = 0x02,then Compensate Loss 31%
|
|
al3006_write_reg(client, ALS_WINDOWS_REG, value);
|
|
|
|
return 0;
|
|
}
|
|
void disable_al3006_device(struct i2c_client *client)
|
|
{
|
|
char value;
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
|
|
#if 0
|
|
mutex_lock(&al3006->lock);
|
|
al3006_read_reg(client, CONFIG_REG, &value);
|
|
value &= ~POWER_MODE_MASK;
|
|
value |= POWER_DOWN_MODE;
|
|
al3006_write_reg(client, CONFIG_REG, value);
|
|
mutex_unlock(&al3006->lock);
|
|
#endif
|
|
mutex_lock(&al3006->lock);
|
|
al3006_write_reg(client, CONFIG_REG, 0x0B);
|
|
al3006_read_reg(client, CONFIG_REG, &value);
|
|
mutex_unlock(&al3006->lock);
|
|
AL3006_DEBUG("%s: value = 0x%x\n", __FUNCTION__,value);
|
|
}
|
|
|
|
void enable_al3006_device(struct i2c_client *client)
|
|
{
|
|
char value;
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
|
|
mutex_lock(&al3006->lock);
|
|
al3006_read_reg(client, CONFIG_REG, &value);
|
|
value &= ~POWER_MODE_MASK;
|
|
value |= POWER_UP_MODE;
|
|
al3006_write_reg(client, CONFIG_REG, value);
|
|
al3006_read_reg(client, CONFIG_REG, &value);
|
|
mutex_unlock(&al3006->lock);
|
|
|
|
AL3006_DEBUG("%s: value = 0x%x\n", __FUNCTION__,value);
|
|
#if 0
|
|
mutex_lock(&al3006->lock);
|
|
al3006_write_reg(client, CONFIG_REG, 0x03);
|
|
mutex_unlock(&al3006->lock);
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
static void al3006_suspend(struct early_suspend *h)
|
|
{
|
|
struct i2c_client *client = container_of(al3006_psensor_misc.parent, struct i2c_client, dev);
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
printk("al3006 early suspend ========================= \n");
|
|
|
|
if (misc_ls_opened)
|
|
al3006_lsensor_disable(client);
|
|
if (misc_ps_opened)
|
|
//al3006_psensor_disable(client);
|
|
enable_irq_wake(al3006->irq);
|
|
else
|
|
disable_al3006_device(client);
|
|
|
|
|
|
//disable_al3006_device(client);
|
|
}
|
|
|
|
static void al3006_resume(struct early_suspend *h)
|
|
{
|
|
struct i2c_client *client = container_of(al3006_psensor_misc.parent, struct i2c_client, dev);
|
|
struct al3006_data *al3006 = (struct al3006_data *)i2c_get_clientdata(client);
|
|
printk("al3006 early resume ======================== \n");
|
|
|
|
if (misc_ps_opened)
|
|
//al3006_psensor_enable(client);
|
|
disable_irq_wake(al3006->irq);
|
|
if (misc_ls_opened)
|
|
al3006_lsensor_enable(client);
|
|
|
|
enable_al3006_device(client);
|
|
}
|
|
#else
|
|
#define al3006_suspend NULL
|
|
#define al3006_resume NULL
|
|
#endif
|
|
|
|
static int al3006_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
{
|
|
struct al3006_data *al3006 = &al3006_struct_data;
|
|
int rc = -EIO;
|
|
char value = 0;
|
|
|
|
printk("\n%s: al3006 i2c client probe\n\n", __FUNCTION__);
|
|
al3006_read_reg(client, CONFIG_REG, &value);
|
|
printk("\n%s: al3006's CONFIG_REG value = 0x%x\n", __FUNCTION__, value);
|
|
|
|
al3006->client = client;
|
|
i2c_set_clientdata(client, al3006);
|
|
mutex_init(&al3006->lock);
|
|
|
|
rc = register_psensor_device(client, al3006);
|
|
if (rc) {
|
|
dev_err(&client->dev, "failed to register_psensor_device\n");
|
|
goto done;
|
|
}
|
|
|
|
rc = register_lsensor_device(client, al3006);
|
|
if (rc) {
|
|
dev_err(&client->dev, "failed to register_lsensor_device\n");
|
|
goto unregister_device1;
|
|
}
|
|
|
|
rc = al3006_config(client);
|
|
if (rc) {
|
|
dev_err(&client->dev, "failed to al3006_config\n");
|
|
goto unregister_device2;
|
|
}
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
al3006_early_suspend.suspend = al3006_suspend;
|
|
al3006_early_suspend.resume = al3006_resume;
|
|
al3006_early_suspend.level = 0x02;
|
|
register_early_suspend(&al3006_early_suspend);
|
|
#endif
|
|
|
|
INIT_DELAYED_WORK(&al3006->dwork, al3006_work_handler);
|
|
|
|
rc = gpio_request(client->irq, "al3006 irq");
|
|
if (rc) {
|
|
pr_err("%s: request gpio %d for al3006 irq failed \n", __FUNCTION__, client->irq);
|
|
goto unregister_device2;
|
|
}
|
|
rc = gpio_direction_input(client->irq);
|
|
if (rc) {
|
|
pr_err("%s: failed set gpio input\n", __FUNCTION__);
|
|
}
|
|
gpio_pull_updown(client->irq, GPIOPullUp);
|
|
al3006->irq = gpio_to_irq(client->irq);
|
|
mdelay(1);
|
|
rc = request_irq(al3006->irq, al3006_irq_handler,
|
|
IRQ_TYPE_EDGE_FALLING, client->name, (void *)al3006);//IRQ_TYPE_LEVEL_LOW
|
|
if (rc < 0) {
|
|
dev_err(&client->dev,"request_irq failed for gpio %d (%d)\n", client->irq, rc);
|
|
goto err_free_gpio;
|
|
}
|
|
|
|
//al3006_psensor_enable(client);
|
|
//al3006_lsensor_enable(client);
|
|
|
|
return 0;
|
|
|
|
err_free_gpio:
|
|
gpio_free(client->irq);
|
|
unregister_device2:
|
|
unregister_lsensor_device(client, &al3006_struct_data);
|
|
unregister_device1:
|
|
unregister_psensor_device(client, &al3006_struct_data);
|
|
done:
|
|
return rc;
|
|
}
|
|
|
|
static int al3006_remove(struct i2c_client *client)
|
|
{
|
|
struct al3006_data *data = i2c_get_clientdata(client);
|
|
|
|
unregister_psensor_device(client, data);
|
|
unregister_lsensor_device(client, data);
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
unregister_early_suspend(&al3006_early_suspend);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id al3006_id[] = {
|
|
{"al3006", 0},
|
|
{ }
|
|
};
|
|
|
|
static struct i2c_driver al3006_driver = {
|
|
.driver = {
|
|
.name = "al3006",
|
|
},
|
|
.probe = al3006_probe,
|
|
.remove = al3006_remove,
|
|
.id_table = al3006_id,
|
|
|
|
};
|
|
|
|
static int __init al3006_init(void)
|
|
{
|
|
|
|
return i2c_add_driver(&al3006_driver);
|
|
}
|
|
|
|
static void __exit al3006_exit(void)
|
|
{
|
|
return i2c_del_driver(&al3006_driver);
|
|
}
|
|
|
|
module_init(al3006_init);
|
|
module_exit(al3006_exit);
|