Loading Documentation/devicetree/bindings/input/misc/ltr553.txt +4 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,9 @@ Required properties: light ratio above 0.99. - liteon,wakeup-threshold : Threshold in centimeter to wake up SoC in suspend mode when proximity is enabled. - liteon,als-sensitivity : Ambient light sensitivity in lux. This array configures how much light intensity change will trigger a light sensor event under different als gain configuration. The array size must be 8. Example: Loading @@ -59,5 +62,6 @@ Required properties: liteon,ps-pulses = <4>; liteon,wakeup-threshold = <4>; liteon,als-integration-time = <0>; liteon,als-sensitivity = <6000 3200 1600 800 0 0 130 1>; }; } drivers/input/misc/ltr553.c +173 −54 Original line number Diff line number Diff line Loading @@ -79,6 +79,10 @@ #define LTR553_CALIBRATE_SAMPLES 15 #define ALS_GAIN_SWITCH_THRESHOLD 60000 #define LTR553_ALS_INVALID(value) (value & 0x80) /* LTR553 ALS data is 16 bit */ #define ALS_DATA_MASK 0xffff #define ALS_LOW_BYTE(data) ((data) & 0xff) Loading Loading @@ -150,7 +154,6 @@ struct ltr553_data { int als_persist; int als_integration_time; int als_measure_rate; int als_sensitivity; int ps_led; int ps_pulses; int ps_measure_rate; Loading Loading @@ -233,6 +236,8 @@ static int ps_mrr_table[] = { 50, 70, 100, 200, 500, 1000, 2000, 10, /* Tuned for devices with rubber */ static int ps_distance_table[] = { 790, 337, 195, 114, 78, 62, 50 }; static int sensitivity_table[] = {150, 150, 100, 100, 0, 0, 100, 1}; static struct sensors_classdev als_cdev = { .name = "ltr553-light", .vendor = "Lite-On Technology Corp", Loading Loading @@ -487,18 +492,16 @@ static int ltr553_parse_dt(struct device *dev, struct ltr553_data *ltr) } /* 4 & 5 are reserved */ if ((value > 0x7) || (value == 0x4) || (value == 0x5)) { dev_err(dev, "liteon,als-integration-time out of range\n"); dev_err(dev, "liteon,als-gain invalid\n"); return -EINVAL; } ltr->als_gain = value; /* als sensitivity */ rc = of_property_read_u32(dp, "liteon,als-sensitivity", &value); if (rc) { dev_err(dev, "read liteon,als-sensitivity failed. Drop to default\n"); value = LTR553_ALS_SENSITIVITY; } ltr->als_sensitivity = value; rc = of_property_read_u32_array(dp, "liteon,als-sensitivity", sensitivity_table, ARRAY_SIZE(sensitivity_table)); if (rc) dev_info(dev, "read liteon,als-sensitivity failed. Drop to default\n"); /* als equation map */ rc = of_property_read_u32_array(dp, "liteon,als-equation-0", Loading Loading @@ -596,6 +599,7 @@ static int ltr553_init_input(struct ltr553_data *ltr) static int ltr553_init_device(struct ltr553_data *ltr) { int rc; unsigned int tmp; /* Enable als/ps interrupt */ rc = regmap_write(ltr->regmap, LTR553_REG_INTERRUPT, Loading Loading @@ -655,6 +659,21 @@ static int ltr553_init_device(struct ltr553_data *ltr) return rc; } /* set up als gain */ rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, &tmp); if (rc) { dev_err(<r->i2c->dev, "read %d register failed\n", LTR553_REG_ALS_CTL); return rc; } rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, (tmp & (~0x1c)) | (ltr->als_gain << 2)); if (rc) { dev_err(<r->i2c->dev, "write %d register failed\n", LTR553_REG_ALS_CTL); return rc; } return 0; } Loading @@ -675,9 +694,9 @@ static int ltr553_calc_lux(int ch0data, int ch1data, int gain, ratio = ch1data * 100 / (ch0data + ch1data); if (ratio < 45) eqtn = &eqtn_map[0]; else if ((ratio >= 45) && (ratio < 68)) else if ((ratio >= 45) && (ratio < 74)) eqtn = &eqtn_map[1]; else if ((ratio >= 68) && (ratio < 99)) else if ((ratio >= 74) && (ratio < 99)) eqtn = &eqtn_map[2]; else eqtn = &eqtn_map[3]; Loading Loading @@ -730,6 +749,123 @@ static int ltr553_calc_adc(int ratio, int lux, int gain, return result <= 0 ? 1 : result; } /* update als gain and threshold */ static int ltr553_als_update_setting(struct ltr553_data *ltr, unsigned int ch0data, unsigned int ch1data, unsigned int als_int_fac) { int gain_index; unsigned int config; unsigned int ratio; unsigned int adc_base; int rc; int adc; int i; u8 als_data[4]; for (i = ARRAY_SIZE(als_gain_table) - 1; i >= 0; i--) { if ((i == 4) || (i == 5)) continue; if ((ch0data + ch1data) * als_gain_table[i] / als_gain_table[ltr->als_gain] < ALS_GAIN_SWITCH_THRESHOLD) break; } gain_index = i < 0 ? 0 : i; /* * Disable als and enable it again to avoid incorrect value. * Updating als gain during als measurement cycle will cause * incorrect light sensor adc value. The logic here is to handle * this scenario. */ if (ltr->als_gain != gain_index) { rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, &config); if (rc) { dev_err(<r->i2c->dev, "read %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; } /* disable als sensor */ rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, config & (~0x1)); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; } /* write new als gain */ rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, (config & (~0x1c)) | (gain_index << 2)); if (rc) { dev_err(<r->i2c->dev, "write %d register failed\n", LTR553_REG_ALS_CTL); return rc; } } if ((ch0data == 0) && (ch1data == 0)) { adc = 1; } else { ratio = ch1data * 100 / (ch0data + ch1data); dev_dbg(<r->i2c->dev, "ratio:%d\n", ratio); adc = ltr553_calc_adc(ratio, sensitivity_table[gain_index], als_gain_table[gain_index], als_int_fac); } dev_dbg(<r->i2c->dev, "adc:%d\n", adc); /* catch'ya! */ adc_base = ch0data * als_gain_table[gain_index] / als_gain_table[ltr->als_gain]; /* upper threshold */ if (adc_base + adc > ALS_DATA_MASK) { als_data[0] = 0xff; als_data[1] = 0xff; } else { als_data[0] = ALS_LOW_BYTE(adc_base + adc); als_data[1] = ALS_HIGH_BYTE(adc_base + adc); } /* lower threshold */ if (adc_base < adc) { als_data[2] = 0x0; als_data[3] = 0x0; } else { als_data[2] = ALS_LOW_BYTE(adc_base - adc); als_data[3] = ALS_HIGH_BYTE(adc_base - adc); } rc = regmap_bulk_write(ltr->regmap, LTR553_REG_ALS_THRES_UP_0, als_data, 4); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_THRES_UP_0, rc); return rc; } if (ltr->als_gain != gain_index) { /* enable als_sensor */ rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, (config & (~0x1c)) | (gain_index << 2) | 0x1); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; } ltr->als_gain = gain_index; } return 0; } static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) { unsigned int als_int_fac; Loading @@ -741,8 +877,6 @@ static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) int lux; unsigned int ch0data; unsigned int ch1data; int adc; int ratio; u8 ps_data[4]; int i; Loading Loading @@ -778,7 +912,15 @@ static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) lux, als_data[0], als_data[1], als_data[2], als_data[3]); if (lux != ltr->last_als) { rc = regmap_read(ltr->regmap, LTR553_REG_ALS_PS_STATUS, &tmp); if (rc) { dev_err(<r->i2c->dev, "read %d failed.(%d)\n", LTR553_REG_ALS_PS_STATUS, rc); goto exit; } if ((lux != ltr->last_als) && (!LTR553_ALS_INVALID(tmp))) { input_report_abs(ltr->input_light, ABS_MISC, lux); input_event(ltr->input_light, EV_SYN, SYN_TIME_SEC, ktime_to_timespec(timestamp).tv_sec); Loading @@ -790,44 +932,21 @@ static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) } ltr->last_als = lux; /* Set up threshold */ tmp = als_data[2] | (als_data[3] << 8); if ((ch0data == 0) && (ch1data == 0)) { adc = 1; } else { ratio = ch1data * 100 / (ch0data + ch1data); dev_dbg(<r->i2c->dev, "ratio:%d\n", ratio); adc = ltr553_calc_adc(ratio, ltr->als_sensitivity, als_gain_table[ltr->als_gain], als_int_fac); } dev_dbg(<r->i2c->dev, "adc:%d\n", adc); dev_dbg(<r->i2c->dev, "previous als_gain:%d\n", ltr->als_gain); /* upper threshold */ if (tmp + adc > ALS_DATA_MASK) { als_data[0] = 0xff; als_data[1] = 0xff; } else { als_data[0] = ALS_LOW_BYTE(tmp + adc); als_data[1] = ALS_HIGH_BYTE(tmp + adc); } /* lower threshold */ if (tmp < adc) { als_data[2] = 0x0; als_data[3] = 0x0; } else { als_data[2] = ALS_LOW_BYTE(tmp - adc); als_data[3] = ALS_HIGH_BYTE(tmp - adc); } rc = regmap_bulk_write(ltr->regmap, LTR553_REG_ALS_THRES_UP_0, als_data, 4); rc = ltr553_als_update_setting(ltr, ch0data, ch1data, als_int_fac); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_THRES_UP_0, rc); dev_err(<r->i2c->dev, "update setting failed\n"); goto exit; } dev_dbg(<r->i2c->dev, "new als_gain:%d\n", ltr->als_gain); } else { /* process ps value */ rc = regmap_bulk_read(ltr->regmap, LTR553_REG_PS_DATA_0, ps_data, 2); Loading Loading @@ -1035,7 +1154,7 @@ static int ltr553_enable_ps(struct ltr553_data *ltr, int enable) if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_PS_CTL, rc); return rc; goto exit; } ltr->ps_enabled = false; Loading Loading @@ -1103,14 +1222,14 @@ static int ltr553_enable_als(struct ltr553_data *ltr, int enable) if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; goto exit; } ltr->als_enabled = false; } exit: return 0; return rc; } static void ltr553_als_enable_work(struct work_struct *work) Loading Loading
Documentation/devicetree/bindings/input/misc/ltr553.txt +4 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,9 @@ Required properties: light ratio above 0.99. - liteon,wakeup-threshold : Threshold in centimeter to wake up SoC in suspend mode when proximity is enabled. - liteon,als-sensitivity : Ambient light sensitivity in lux. This array configures how much light intensity change will trigger a light sensor event under different als gain configuration. The array size must be 8. Example: Loading @@ -59,5 +62,6 @@ Required properties: liteon,ps-pulses = <4>; liteon,wakeup-threshold = <4>; liteon,als-integration-time = <0>; liteon,als-sensitivity = <6000 3200 1600 800 0 0 130 1>; }; }
drivers/input/misc/ltr553.c +173 −54 Original line number Diff line number Diff line Loading @@ -79,6 +79,10 @@ #define LTR553_CALIBRATE_SAMPLES 15 #define ALS_GAIN_SWITCH_THRESHOLD 60000 #define LTR553_ALS_INVALID(value) (value & 0x80) /* LTR553 ALS data is 16 bit */ #define ALS_DATA_MASK 0xffff #define ALS_LOW_BYTE(data) ((data) & 0xff) Loading Loading @@ -150,7 +154,6 @@ struct ltr553_data { int als_persist; int als_integration_time; int als_measure_rate; int als_sensitivity; int ps_led; int ps_pulses; int ps_measure_rate; Loading Loading @@ -233,6 +236,8 @@ static int ps_mrr_table[] = { 50, 70, 100, 200, 500, 1000, 2000, 10, /* Tuned for devices with rubber */ static int ps_distance_table[] = { 790, 337, 195, 114, 78, 62, 50 }; static int sensitivity_table[] = {150, 150, 100, 100, 0, 0, 100, 1}; static struct sensors_classdev als_cdev = { .name = "ltr553-light", .vendor = "Lite-On Technology Corp", Loading Loading @@ -487,18 +492,16 @@ static int ltr553_parse_dt(struct device *dev, struct ltr553_data *ltr) } /* 4 & 5 are reserved */ if ((value > 0x7) || (value == 0x4) || (value == 0x5)) { dev_err(dev, "liteon,als-integration-time out of range\n"); dev_err(dev, "liteon,als-gain invalid\n"); return -EINVAL; } ltr->als_gain = value; /* als sensitivity */ rc = of_property_read_u32(dp, "liteon,als-sensitivity", &value); if (rc) { dev_err(dev, "read liteon,als-sensitivity failed. Drop to default\n"); value = LTR553_ALS_SENSITIVITY; } ltr->als_sensitivity = value; rc = of_property_read_u32_array(dp, "liteon,als-sensitivity", sensitivity_table, ARRAY_SIZE(sensitivity_table)); if (rc) dev_info(dev, "read liteon,als-sensitivity failed. Drop to default\n"); /* als equation map */ rc = of_property_read_u32_array(dp, "liteon,als-equation-0", Loading Loading @@ -596,6 +599,7 @@ static int ltr553_init_input(struct ltr553_data *ltr) static int ltr553_init_device(struct ltr553_data *ltr) { int rc; unsigned int tmp; /* Enable als/ps interrupt */ rc = regmap_write(ltr->regmap, LTR553_REG_INTERRUPT, Loading Loading @@ -655,6 +659,21 @@ static int ltr553_init_device(struct ltr553_data *ltr) return rc; } /* set up als gain */ rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, &tmp); if (rc) { dev_err(<r->i2c->dev, "read %d register failed\n", LTR553_REG_ALS_CTL); return rc; } rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, (tmp & (~0x1c)) | (ltr->als_gain << 2)); if (rc) { dev_err(<r->i2c->dev, "write %d register failed\n", LTR553_REG_ALS_CTL); return rc; } return 0; } Loading @@ -675,9 +694,9 @@ static int ltr553_calc_lux(int ch0data, int ch1data, int gain, ratio = ch1data * 100 / (ch0data + ch1data); if (ratio < 45) eqtn = &eqtn_map[0]; else if ((ratio >= 45) && (ratio < 68)) else if ((ratio >= 45) && (ratio < 74)) eqtn = &eqtn_map[1]; else if ((ratio >= 68) && (ratio < 99)) else if ((ratio >= 74) && (ratio < 99)) eqtn = &eqtn_map[2]; else eqtn = &eqtn_map[3]; Loading Loading @@ -730,6 +749,123 @@ static int ltr553_calc_adc(int ratio, int lux, int gain, return result <= 0 ? 1 : result; } /* update als gain and threshold */ static int ltr553_als_update_setting(struct ltr553_data *ltr, unsigned int ch0data, unsigned int ch1data, unsigned int als_int_fac) { int gain_index; unsigned int config; unsigned int ratio; unsigned int adc_base; int rc; int adc; int i; u8 als_data[4]; for (i = ARRAY_SIZE(als_gain_table) - 1; i >= 0; i--) { if ((i == 4) || (i == 5)) continue; if ((ch0data + ch1data) * als_gain_table[i] / als_gain_table[ltr->als_gain] < ALS_GAIN_SWITCH_THRESHOLD) break; } gain_index = i < 0 ? 0 : i; /* * Disable als and enable it again to avoid incorrect value. * Updating als gain during als measurement cycle will cause * incorrect light sensor adc value. The logic here is to handle * this scenario. */ if (ltr->als_gain != gain_index) { rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, &config); if (rc) { dev_err(<r->i2c->dev, "read %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; } /* disable als sensor */ rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, config & (~0x1)); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; } /* write new als gain */ rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, (config & (~0x1c)) | (gain_index << 2)); if (rc) { dev_err(<r->i2c->dev, "write %d register failed\n", LTR553_REG_ALS_CTL); return rc; } } if ((ch0data == 0) && (ch1data == 0)) { adc = 1; } else { ratio = ch1data * 100 / (ch0data + ch1data); dev_dbg(<r->i2c->dev, "ratio:%d\n", ratio); adc = ltr553_calc_adc(ratio, sensitivity_table[gain_index], als_gain_table[gain_index], als_int_fac); } dev_dbg(<r->i2c->dev, "adc:%d\n", adc); /* catch'ya! */ adc_base = ch0data * als_gain_table[gain_index] / als_gain_table[ltr->als_gain]; /* upper threshold */ if (adc_base + adc > ALS_DATA_MASK) { als_data[0] = 0xff; als_data[1] = 0xff; } else { als_data[0] = ALS_LOW_BYTE(adc_base + adc); als_data[1] = ALS_HIGH_BYTE(adc_base + adc); } /* lower threshold */ if (adc_base < adc) { als_data[2] = 0x0; als_data[3] = 0x0; } else { als_data[2] = ALS_LOW_BYTE(adc_base - adc); als_data[3] = ALS_HIGH_BYTE(adc_base - adc); } rc = regmap_bulk_write(ltr->regmap, LTR553_REG_ALS_THRES_UP_0, als_data, 4); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_THRES_UP_0, rc); return rc; } if (ltr->als_gain != gain_index) { /* enable als_sensor */ rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, (config & (~0x1c)) | (gain_index << 2) | 0x1); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; } ltr->als_gain = gain_index; } return 0; } static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) { unsigned int als_int_fac; Loading @@ -741,8 +877,6 @@ static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) int lux; unsigned int ch0data; unsigned int ch1data; int adc; int ratio; u8 ps_data[4]; int i; Loading Loading @@ -778,7 +912,15 @@ static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) lux, als_data[0], als_data[1], als_data[2], als_data[3]); if (lux != ltr->last_als) { rc = regmap_read(ltr->regmap, LTR553_REG_ALS_PS_STATUS, &tmp); if (rc) { dev_err(<r->i2c->dev, "read %d failed.(%d)\n", LTR553_REG_ALS_PS_STATUS, rc); goto exit; } if ((lux != ltr->last_als) && (!LTR553_ALS_INVALID(tmp))) { input_report_abs(ltr->input_light, ABS_MISC, lux); input_event(ltr->input_light, EV_SYN, SYN_TIME_SEC, ktime_to_timespec(timestamp).tv_sec); Loading @@ -790,44 +932,21 @@ static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) } ltr->last_als = lux; /* Set up threshold */ tmp = als_data[2] | (als_data[3] << 8); if ((ch0data == 0) && (ch1data == 0)) { adc = 1; } else { ratio = ch1data * 100 / (ch0data + ch1data); dev_dbg(<r->i2c->dev, "ratio:%d\n", ratio); adc = ltr553_calc_adc(ratio, ltr->als_sensitivity, als_gain_table[ltr->als_gain], als_int_fac); } dev_dbg(<r->i2c->dev, "adc:%d\n", adc); dev_dbg(<r->i2c->dev, "previous als_gain:%d\n", ltr->als_gain); /* upper threshold */ if (tmp + adc > ALS_DATA_MASK) { als_data[0] = 0xff; als_data[1] = 0xff; } else { als_data[0] = ALS_LOW_BYTE(tmp + adc); als_data[1] = ALS_HIGH_BYTE(tmp + adc); } /* lower threshold */ if (tmp < adc) { als_data[2] = 0x0; als_data[3] = 0x0; } else { als_data[2] = ALS_LOW_BYTE(tmp - adc); als_data[3] = ALS_HIGH_BYTE(tmp - adc); } rc = regmap_bulk_write(ltr->regmap, LTR553_REG_ALS_THRES_UP_0, als_data, 4); rc = ltr553_als_update_setting(ltr, ch0data, ch1data, als_int_fac); if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_THRES_UP_0, rc); dev_err(<r->i2c->dev, "update setting failed\n"); goto exit; } dev_dbg(<r->i2c->dev, "new als_gain:%d\n", ltr->als_gain); } else { /* process ps value */ rc = regmap_bulk_read(ltr->regmap, LTR553_REG_PS_DATA_0, ps_data, 2); Loading Loading @@ -1035,7 +1154,7 @@ static int ltr553_enable_ps(struct ltr553_data *ltr, int enable) if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_PS_CTL, rc); return rc; goto exit; } ltr->ps_enabled = false; Loading Loading @@ -1103,14 +1222,14 @@ static int ltr553_enable_als(struct ltr553_data *ltr, int enable) if (rc) { dev_err(<r->i2c->dev, "write %d failed.(%d)\n", LTR553_REG_ALS_CTL, rc); return rc; goto exit; } ltr->als_enabled = false; } exit: return 0; return rc; } static void ltr553_als_enable_work(struct work_struct *work) Loading