diff options
Diffstat (limited to 'drivers/sensor/gp2a_proximity.c')
-rw-r--r-- | drivers/sensor/gp2a_proximity.c | 324 |
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], ®); + THR_REG_MSB(thrd, reg); + opt_i2c_write(cal_image[4][0], ®); + + 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], ®); + THR_REG_MSB(thrd, reg); + opt_i2c_write(cal_image[6][0], ®); + + /* 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], ®); + THR_REG_MSB(thrd, reg); + opt_i2c_write(manual_image[4][0], ®); + + thrd = manual_image[5][1]+(data->offset_value); + THR_REG_LSB(thrd, reg); + opt_i2c_write(manual_image[5][0], ®); + THR_REG_MSB(thrd, reg); + opt_i2c_write(manual_image[6][0], ®); + + /* 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; |