aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sensor/gp2a_proximity.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sensor/gp2a_proximity.c')
-rw-r--r--drivers/sensor/gp2a_proximity.c324
1 files changed, 322 insertions, 2 deletions
diff --git a/drivers/sensor/gp2a_proximity.c b/drivers/sensor/gp2a_proximity.c
index 012d8b8..7efbf3e 100644
--- a/drivers/sensor/gp2a_proximity.c
+++ b/drivers/sensor/gp2a_proximity.c
@@ -47,6 +47,8 @@
#define VENDOR "SHARP"
#define CHIP_ID "GP2AP"
+#define OFFSET_FILE_PATH "/efs/prox_cal"
+
#if 1
#define gprintk(fmt, x...) printk(KERN_INFO "%s(%d): "\
fmt, __func__ , __LINE__, ## x)
@@ -54,7 +56,11 @@ fmt, __func__ , __LINE__, ## x)
#define gprintk(x...) do { } while (0)
#endif
/**************************************************/
+#if defined(CONFIG_MACH_BAFFIN)
+#define PROX_READ_NUM 20
+#else
#define PROX_READ_NUM 40
+#endif
#define PS_LOW_THD_L 0x08
#define PS_LOW_THD_H 0x09
@@ -72,6 +78,7 @@ static char proximity_avg_on;
struct gp2a_data {
struct input_dev *input_dev;
struct work_struct work; /* for proximity sensor */
+ struct mutex data_mutex;
struct device *proximity_dev;
struct gp2a_platform_data *pdata;
struct wake_lock prx_wake_lock;
@@ -83,6 +90,12 @@ struct gp2a_data {
int irq;
int average[3]; /*for proximity adc average */
ktime_t prox_poll_delay;
+
+ /* Auto Calibration */
+ int offset_value;
+ int cal_result;
+ uint16_t threshold_high;
+ bool offset_cal_high;
};
/* initial value for sensor register */
@@ -100,7 +113,11 @@ static u8 gp2a_original_image_030a[COL][2] = {
/* {0x05 , 0x00}, */
/* {0x06 , 0xFF}, */
/* {0x07 , 0xFF}, */
+#if defined(CONFIG_MACH_BAFFIN)
+ {0x08, 0x08},
+#else
{0x08, 0x09}, /*PS mode LTH(Loff): (??mm) */
+#endif
{0x09, 0x00}, /*PS mode LTH(Loff) : */
{0x0A, 0x0A}, /*PS mode HTH(Lon) : (??mm) */
{0x0B, 0x00}, /* PS mode HTH(Lon) : */
@@ -133,6 +150,15 @@ static u8 gp2a_original_image[COL][2] = {
{0x00, 0xC0}
};
+#define THR_REG_LSB(data, reg) \
+ { \
+ reg = (u8)data & 0xff; \
+ }
+#define THR_REG_MSB(data, reg) \
+ { \
+ reg = (u8)data >> 8; \
+ }
+
static int proximity_onoff(u8 onoff);
int is_gp2a030a(void)
@@ -200,6 +226,231 @@ static int gp2a_update_threshold(u8 (*selected_image)[2],
return err;
}
+static int proximity_adc_read(struct gp2a_data *data)
+{
+ int sum[OFFSET_ARRAY_LENGTH];
+ int i = OFFSET_ARRAY_LENGTH-1;
+ int avg;
+ int min;
+ int max;
+ int total;
+ int D2_data;
+ unsigned char get_D2_data[2];
+
+ mutex_lock(&data->data_mutex);
+ do {
+ msleep(50);
+ opt_i2c_read(DATA2_LSB, get_D2_data, sizeof(get_D2_data));
+ D2_data = (get_D2_data[1] << 8) | get_D2_data[0];
+ sum[i] = D2_data;
+ if (i == 0) {
+ min = sum[i];
+ max = sum[i];
+ } else {
+ if (sum[i] < min)
+ min = sum[i];
+ else if (sum[i] > max)
+ max = sum[i];
+ }
+ total += sum[i];
+ } while (i--);
+ mutex_unlock(&data->data_mutex);
+
+ total -= (min + max);
+ avg = (int)(total / (OFFSET_ARRAY_LENGTH - 2));
+ pr_info("%s: offset = %d\n", __func__, avg);
+
+ return avg;
+}
+
+
+static int proximity_open_calibration(struct gp2a_data *data)
+{
+ struct file *cal_filp = NULL;
+ int err = 0;
+ mm_segment_t old_fs;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(OFFSET_FILE_PATH,
+ O_RDONLY, S_IRUGO | S_IWUSR | S_IWGRP);
+
+ if (IS_ERR(cal_filp)) {
+ pr_err("%s: Can't open calibration file\n", __func__);
+ set_fs(old_fs);
+ err = PTR_ERR(cal_filp);
+ goto done;
+ }
+
+ err = cal_filp->f_op->read(cal_filp,
+ (char *)&data->offset_value,
+ sizeof(int), &cal_filp->f_pos);
+ if (err != sizeof(int)) {
+ pr_err("%s: Can't read the cal data from file\n", __func__);
+ err = -EIO;
+ }
+
+ pr_info("%s: (%d)\n", __func__,
+ data->offset_value);
+
+ filp_close(cal_filp, current->files);
+done:
+ set_fs(old_fs);
+ return err;
+}
+
+static int proximity_do_calibrate(struct gp2a_data *data,
+ bool do_calib, bool thresh_set)
+{
+ struct file *cal_filp;
+ int err;
+ int xtalk_avg = 0;
+ int offset_change = 0;
+ uint16_t thrd = 0;
+ u8 reg;
+ mm_segment_t old_fs;
+
+ u8 (*cal_image)[2] = (is_gp2a030a() ?
+ gp2a_original_image_030a : gp2a_original_image);
+
+ if (do_calib) {
+ if (thresh_set) {
+ /* for proximity_thresh_store */
+ data->offset_value =
+ data->threshold_high -
+ (cal_image[6][1] << 8 | cal_image[5][1]);
+ } else {
+ /* tap offset button */
+ /* get offset value */
+ xtalk_avg = proximity_adc_read(data);
+ offset_change =
+ (cal_image[6][1] << 8 | cal_image[5][1])
+ - DEFAULT_HI_THR;
+ if (xtalk_avg < offset_change) {
+ /* do not need calibration */
+ data->cal_result = 0;
+ err = 0;
+ goto no_cal;
+ }
+ data->offset_value = xtalk_avg - offset_change;
+ }
+ /* update threshold */
+ thrd = (cal_image[4][1] << 8 | cal_image[3][1])
+ + (data->offset_value);
+ THR_REG_LSB(thrd, reg);
+ opt_i2c_write(cal_image[3][0], &reg);
+ THR_REG_MSB(thrd, reg);
+ opt_i2c_write(cal_image[4][0], &reg);
+
+ thrd = (cal_image[4][1] << 8 | cal_image[5][1])
+ +(data->offset_value);
+ THR_REG_LSB(thrd, reg);
+ opt_i2c_write(cal_image[5][0], &reg);
+ THR_REG_MSB(thrd, reg);
+ opt_i2c_write(cal_image[6][0], &reg);
+
+ /* calibration result */
+ if (!thresh_set)
+ data->cal_result = 1;
+ } else {
+ /* tap reset button */
+ data->offset_value = 0;
+ /* update threshold */
+ opt_i2c_write(cal_image[3][0], &cal_image[3][1]);
+ opt_i2c_write(cal_image[4][0], &cal_image[4][1]);
+ opt_i2c_write(cal_image[5][0], &cal_image[5][1]);
+ opt_i2c_write(cal_image[6][0], &cal_image[6][1]);
+ /* calibration result */
+ data->cal_result = 2;
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(OFFSET_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+ if (IS_ERR(cal_filp)) {
+ pr_err("%s: Can't open calibration file\n", __func__);
+ set_fs(old_fs);
+ err = PTR_ERR(cal_filp);
+ goto done;
+ }
+
+ err = cal_filp->f_op->write(cal_filp,
+ (char *)&data->offset_value, sizeof(int),
+ &cal_filp->f_pos);
+ if (err != sizeof(int)) {
+ pr_err("%s: Can't write the cal data to file\n", __func__);
+ err = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+done:
+ set_fs(old_fs);
+no_cal:
+ return err;
+}
+
+static int proximity_manual_offset(struct gp2a_data *data, u8 change_on)
+{
+ struct file *cal_filp;
+ int err;
+ int16_t thrd;
+ u8 reg;
+ mm_segment_t old_fs;
+
+ u8 (*manual_image)[2] = (is_gp2a030a() ?
+ gp2a_original_image_030a : gp2a_original_image);
+
+ data->offset_value = change_on;
+ /* update threshold */
+ thrd = manual_image[3][1]+(data->offset_value);
+ THR_REG_LSB(thrd, reg);
+ opt_i2c_write(manual_image[3][0], &reg);
+ THR_REG_MSB(thrd, reg);
+ opt_i2c_write(manual_image[4][0], &reg);
+
+ thrd = manual_image[5][1]+(data->offset_value);
+ THR_REG_LSB(thrd, reg);
+ opt_i2c_write(manual_image[5][0], &reg);
+ THR_REG_MSB(thrd, reg);
+ opt_i2c_write(manual_image[6][0], &reg);
+
+ /* calibration result */
+ data->cal_result = 1;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(OFFSET_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+ if (IS_ERR(cal_filp)) {
+ pr_err("%s: Can't open calibration file\n", __func__);
+ set_fs(old_fs);
+ err = PTR_ERR(cal_filp);
+ goto done;
+ }
+
+ err = cal_filp->f_op->write(cal_filp,
+ (char *)&data->offset_value, sizeof(int),
+ &cal_filp->f_pos);
+ if (err != sizeof(int)) {
+ pr_err("%s: Can't write the cal data to file\n", __func__);
+ err = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+done:
+ set_fs(old_fs);
+ return err;
+}
+
+
/* Proximity Sysfs interface */
static ssize_t
proximity_enable_show(struct device *dev,
@@ -263,14 +514,23 @@ proximity_enable_store(struct device *dev,
static ssize_t proximity_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct gp2a_data *data = dev_get_drvdata(dev);
+ static int count; /*count for proximity average */
+
int D2_data = 0;
unsigned char get_D2_data[2] = { 0, };
- msleep(20);
+ mutex_lock(&data->data_mutex);
opt_i2c_read(0x10, get_D2_data, sizeof(get_D2_data));
+ mutex_unlock(&data->data_mutex);
D2_data = (get_D2_data[1] << 8) | get_D2_data[0];
- return sprintf(buf, "%d\n", D2_data);
+ data->average[count] = D2_data;
+ count++;
+ if (count == PROX_READ_NUM)
+ count = 0;
+ pr_debug("%s: D2_data = %d\n", __func__, D2_data);
+ return snprintf(buf, PAGE_SIZE, "%d\n", D2_data);
}
static ssize_t proximity_avg_show(struct device *dev,
@@ -372,11 +632,53 @@ static ssize_t proximity_thresh_store(struct device *dev,
return size;
}
+static ssize_t proximity_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gp2a_data *data = dev_get_drvdata(dev);
+ int thresh_hi;
+ unsigned char get_D2_data[2];
+
+ msleep(20);
+ opt_i2c_read(PS_HT_LSB, get_D2_data, sizeof(get_D2_data));
+ thresh_hi = (get_D2_data[1] << 8) | get_D2_data[0];
+ data->threshold_high = thresh_hi;
+ return sprintf(buf, "%d,%d\n",
+ data->offset_value, data->threshold_high);
+}
+
+static ssize_t proximity_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct gp2a_data *data = dev_get_drvdata(dev);
+ bool do_calib;
+ int err;
+
+ if (sysfs_streq(buf, "1")) { /* calibrate cancelation value */
+ do_calib = true;
+ } else if (sysfs_streq(buf, "0")) { /* reset cancelation value */
+ do_calib = false;
+ } else {
+ pr_err("%s: invalid value %d\n", __func__, *buf);
+ err = -EINVAL;
+ goto done;
+ }
+ err = proximity_do_calibrate(data, do_calib, false);
+ if (err < 0) {
+ pr_err("%s: proximity_store_offset() failed\n", __func__);
+ goto done;
+ }
+done:
+ return err;
+}
+
static DEVICE_ATTR(enable, 0664, proximity_enable_show, proximity_enable_store);
static DEVICE_ATTR(prox_avg, 0664, proximity_avg_show, proximity_avg_store);
static DEVICE_ATTR(state, 0664, proximity_state_show, NULL);
static DEVICE_ATTR(prox_thresh, S_IRUGO | S_IWUSR,
proximity_thresh_show, proximity_thresh_store);
+static DEVICE_ATTR(prox_cal, 0664, proximity_cal_show, proximity_cal_store);
static struct attribute *proximity_attributes[] = {
&dev_attr_enable.attr,
@@ -411,9 +713,12 @@ static ssize_t proximity_raw_data_show(struct device *dev,
{
int D2_data = 0;
unsigned char get_D2_data[2] = { 0, };
+ struct gp2a_data *data = dev_get_drvdata(dev);
msleep(20);
+ mutex_lock(&data->data_mutex);
opt_i2c_read(0x10, get_D2_data, sizeof(get_D2_data));
+ mutex_unlock(&data->data_mutex);
D2_data = (get_D2_data[1] << 8) | get_D2_data[0];
return sprintf(buf, "%d\n", D2_data);
@@ -430,6 +735,8 @@ static void proxsensor_get_avgvalue(struct gp2a_data *data)
u8 proximity_value = 0;
unsigned char get_D2_data[2] = { 0, };
+ mutex_lock(&data->data_mutex);
+
for (i = 0; i < PROX_READ_NUM; i++) {
msleep(20);
opt_i2c_read(0x10, get_D2_data, sizeof(get_D2_data));
@@ -444,6 +751,7 @@ static void proxsensor_get_avgvalue(struct gp2a_data *data)
if (proximity_value > max)
max = proximity_value;
}
+ mutex_unlock(&data->data_mutex);
avg /= PROX_READ_NUM;
data->average[0] = min;
@@ -761,6 +1069,8 @@ static int gp2a_opt_probe(struct platform_device *pdev)
/* set platdata */
platform_set_drvdata(pdev, gp2a);
+ mutex_init(&gp2a->data_mutex);
+
/* wake lock init */
wake_lock_init(&gp2a->prx_wake_lock, WAKE_LOCK_SUSPEND,
"prx_wake_lock");
@@ -838,6 +1148,12 @@ static int gp2a_opt_probe(struct platform_device *pdev)
goto err_proximity_device_create_file6;
}
+ if (device_create_file(gp2a->proximity_dev, &dev_attr_prox_cal) < 0) {
+ pr_err("%s: could not create device file(%s)!\n", __func__,
+ dev_attr_prox_cal.attr.name);
+ goto err_proximity_device_create_file7;
+ }
+
#ifdef CONFIG_SLP
device_init_wakeup(gp2a->proximity_dev, true);
#endif
@@ -849,6 +1165,8 @@ static int gp2a_opt_probe(struct platform_device *pdev)
return 0;
+err_proximity_device_create_file7:
+ device_remove_file(gp2a->proximity_dev, &dev_attr_prox_cal);
err_proximity_device_create_file6:
device_remove_file(gp2a->proximity_dev, &dev_attr_raw_data);
err_proximity_device_create_file5:
@@ -868,6 +1186,7 @@ err_no_device:
sysfs_remove_group(&gp2a->input_dev->dev.kobj,
&proximity_attribute_group);
wake_lock_destroy(&gp2a->prx_wake_lock);
+ mutex_destroy(&gp2a->data_mutex);
err_sysfs_create_group_proximity:
input_unregister_device(gp2a->input_dev);
error_setup_reg:
@@ -923,6 +1242,7 @@ static int gp2a_opt_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
free_irq(gp2a->irq, gp2a);
gpio_free(gp2a->pdata->p_out);
+ mutex_destroy(&gp2a->data_mutex);
kfree(gp2a);
return 0;