From c259c6853eaee392d81733bcc33445aab5401850 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Tue, 23 Jan 2018 18:15:46 -0600 Subject: [PATCH] misc: cs40l20: Add diagnostics support Added support for diagnostics stimulus generation as well as f0 and ReDC reporting. Change-Id: I0a496501cc229026897865cca2333ba0a70f8bf0 Signed-off-by: Jeff LaBundy Signed-off-by: Ravi Vembu --- drivers/misc/cs40l20.c | 187 +++++++++++++++++++++++++++++++++++------ drivers/misc/cs40l20.h | 13 ++- 2 files changed, 171 insertions(+), 29 deletions(-) diff --git a/drivers/misc/cs40l20.c b/drivers/misc/cs40l20.c index 8e586bc21a23..a665e116946c 100644 --- a/drivers/misc/cs40l20.c +++ b/drivers/misc/cs40l20.c @@ -51,11 +51,15 @@ struct cs40l20_private { struct workqueue_struct *vibe_workqueue; struct mutex lock; unsigned int cp_trigger_index; + unsigned int cp_trailer_index; unsigned int num_waves; bool vibe_init_success; struct gpio_desc *reset_gpio; struct cs40l20_platform_data pdata; struct list_head coeff_desc_head; + unsigned char diag_state; + unsigned int f0_measured; + unsigned int redc_measured; #ifdef CONFIG_ANDROID_TIMED_OUTPUT struct timed_output_dev timed_dev; struct hrtimer vibe_timer; @@ -100,7 +104,7 @@ static ssize_t cs40l20_cp_trigger_index_store(struct device *dev, if (ret) return -EINVAL; - if ((index & 0x7FFF) > (cs40l20->num_waves - 1)) + if ((index & 0x7FFF) > (cs40l20->num_waves - 1) && index != 0xFFFF) return -EINVAL; cs40l20->cp_trigger_index = index; @@ -230,21 +234,20 @@ static ssize_t cs40l20_f0_measured_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l20_private *cs40l20 = cs40l20_get_private(dev); - int ret; - unsigned int val; + unsigned char diag_state; + unsigned int f0_measured; mutex_lock(&cs40l20->lock); - ret = regmap_read(cs40l20->regmap, - cs40l20_dsp_reg(cs40l20, "F0_MEASURED", - CS40L20_XM_UNPACKED_TYPE), &val); + + diag_state = cs40l20->diag_state; + f0_measured = cs40l20->f0_measured; + mutex_unlock(&cs40l20->lock); - if (ret) { - pr_err("Failed to read measured f0\n"); - return ret; - } + if (diag_state != CS40L20_DIAG_STATE_DONE) + return -ENODATA; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return snprintf(buf, PAGE_SIZE, "%d\n", f0_measured); } static ssize_t cs40l20_f0_stored_show(struct device *dev, @@ -294,6 +297,26 @@ static ssize_t cs40l20_f0_stored_store(struct device *dev, return count; } +static ssize_t cs40l20_redc_measured_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l20_private *cs40l20 = cs40l20_get_private(dev); + unsigned char diag_state; + unsigned int redc_measured; + + mutex_lock(&cs40l20->lock); + + diag_state = cs40l20->diag_state; + redc_measured = cs40l20->redc_measured; + + mutex_unlock(&cs40l20->lock); + + if (diag_state != CS40L20_DIAG_STATE_DONE) + return -ENODATA; + + return snprintf(buf, PAGE_SIZE, "%d\n", redc_measured); +} + static DEVICE_ATTR(cp_trigger_index, 0660, cs40l20_cp_trigger_index_show, cs40l20_cp_trigger_index_store); static DEVICE_ATTR(gpio1_rise_index, 0660, cs40l20_gpio1_rise_index_show, @@ -303,6 +326,7 @@ static DEVICE_ATTR(gpio1_fall_index, 0660, cs40l20_gpio1_fall_index_show, static DEVICE_ATTR(f0_measured, 0660, cs40l20_f0_measured_show, NULL); static DEVICE_ATTR(f0_stored, 0660, cs40l20_f0_stored_show, cs40l20_f0_stored_store); +static DEVICE_ATTR(redc_measured, 0660, cs40l20_redc_measured_show, NULL); static struct attribute *cs40l20_dev_attrs[] = { &dev_attr_cp_trigger_index.attr, @@ -310,6 +334,7 @@ static struct attribute *cs40l20_dev_attrs[] = { &dev_attr_gpio1_fall_index.attr, &dev_attr_f0_measured.attr, &dev_attr_f0_stored.attr, + &dev_attr_redc_measured.attr, NULL, }; @@ -317,30 +342,91 @@ static struct attribute_group cs40l20_dev_attr_group = { .attrs = cs40l20_dev_attrs, }; +static int cs40l20_diag_capture(struct cs40l20_private *cs40l20) +{ + struct regmap *regmap = cs40l20->regmap; + int ret; + + if (cs40l20->diag_state != CS40L20_DIAG_STATE_CLOSED_LOOP) + return -ENODATA; + + ret = regmap_read(regmap, cs40l20_dsp_reg(cs40l20, "F0", + CS40L20_XM_UNPACKED_TYPE), &cs40l20->f0_measured); + if (ret) + return ret; + + ret = regmap_read(regmap, cs40l20_dsp_reg(cs40l20, "REDC", + CS40L20_XM_UNPACKED_TYPE), &cs40l20->redc_measured); + if (ret) + return ret; + + cs40l20->diag_state = CS40L20_DIAG_STATE_DONE; + + return 0; +} + static void cs40l20_vibe_start_worker(struct work_struct *work) { struct cs40l20_private *cs40l20 = container_of(work, struct cs40l20_private, vibe_start_work); + struct regmap *regmap = cs40l20->regmap; + struct device *dev = cs40l20->dev; int ret; - unsigned int control_reg; mutex_lock(&cs40l20->lock); - switch (cs40l20->cp_trigger_index) { - case 0x0001 ... 0x7FFF: - control_reg = cs40l20_dsp_reg(cs40l20, "TRIGGERINDEX", - CS40L20_XM_UNPACKED_TYPE); + cs40l20->cp_trailer_index = cs40l20->cp_trigger_index; + + switch (cs40l20->cp_trailer_index) { + case 0x0000: + case 0x8000 ... 0xFFFE: + ret = regmap_write(regmap, + cs40l20_dsp_reg(cs40l20, "TRIGGER_MS", + CS40L20_XM_UNPACKED_TYPE), + cs40l20->cp_trailer_index & 0x7FFF); + if (ret) + dev_err(dev, "Failed to start playback\n"); break; + + case 0x0001 ... 0x7FFF: + ret = regmap_write(regmap, + cs40l20_dsp_reg(cs40l20, "TRIGGERINDEX", + CS40L20_XM_UNPACKED_TYPE), + cs40l20->cp_trailer_index); + if (ret) + dev_err(dev, "Failed to start playback\n"); + break; + + case 0xFFFF: + cs40l20->diag_state = CS40L20_DIAG_STATE_INIT; + + ret = regmap_write(regmap, + cs40l20_dsp_reg(cs40l20, "STIMULUS_MODE", + CS40L20_XM_UNPACKED_TYPE), 1); + if (ret) { + dev_err(dev, "Failed to enable stimulus mode\n"); + goto err_mutex; + } + cs40l20->diag_state = CS40L20_DIAG_STATE_OPEN_LOOP; + + msleep(CS40L20_DIAG_STATE_DELAY_MS); + + ret = regmap_write(regmap, + cs40l20_dsp_reg(cs40l20, "CLOSED_LOOP", + CS40L20_XM_UNPACKED_TYPE), 1); + if (ret) { + dev_err(dev, "Failed to enable closed-loop mode\n"); + goto err_mutex; + } + cs40l20->diag_state = CS40L20_DIAG_STATE_CLOSED_LOOP; + + break; + default: - control_reg = cs40l20_dsp_reg(cs40l20, "TRIGGER_MS", - CS40L20_XM_UNPACKED_TYPE); + dev_err(dev, "Invalid wavetable index\n"); } - ret = regmap_write(cs40l20->regmap, - control_reg, (cs40l20->cp_trigger_index & 0x7FFF)); - if (ret) - dev_err(cs40l20->dev, "Failed to start playback\n"); - +err_mutex: mutex_unlock(&cs40l20->lock); } @@ -348,15 +434,40 @@ static void cs40l20_vibe_stop_worker(struct work_struct *work) { struct cs40l20_private *cs40l20 = container_of(work, struct cs40l20_private, vibe_stop_work); + struct regmap *regmap = cs40l20->regmap; + struct device *dev = cs40l20->dev; int ret; mutex_lock(&cs40l20->lock); - ret = regmap_write(cs40l20->regmap, - cs40l20_dsp_reg(cs40l20, "ENDPLAYBACK", - CS40L20_XM_UNPACKED_TYPE), 1); - if (ret) - dev_err(cs40l20->dev, "Failed to stop playback\n"); + switch (cs40l20->cp_trailer_index) { + case 0xFFFF: + ret = cs40l20_diag_capture(cs40l20); + if (ret) + dev_err(dev, "Failed to capture f0 and ReDC\n"); + + ret = regmap_write(regmap, + cs40l20_dsp_reg(cs40l20, "STIMULUS_MODE", + CS40L20_XM_UNPACKED_TYPE), 0); + if (ret) + dev_err(dev, "Failed to disable stimulus mode\n"); + + ret = regmap_write(regmap, + cs40l20_dsp_reg(cs40l20, "CLOSED_LOOP", + CS40L20_XM_UNPACKED_TYPE), 0); + if (ret) + dev_err(dev, "Failed to disable closed-loop mode\n"); + break; + + default: + ret = regmap_write(regmap, + cs40l20_dsp_reg(cs40l20, "ENDPLAYBACK", + CS40L20_XM_UNPACKED_TYPE), 1); + if (ret) + dev_err(dev, "Failed to stop playback\n"); + } + + cs40l20->cp_trailer_index = 0; mutex_unlock(&cs40l20->lock); } @@ -1017,7 +1128,9 @@ static int cs40l20_init(struct cs40l20_private *cs40l20) struct device *dev = cs40l20->dev; cs40l20->cp_trigger_index = 0; + cs40l20->cp_trailer_index = 0; cs40l20->vibe_init_success = false; + cs40l20->diag_state = CS40L20_DIAG_STATE_INIT; if (cs40l20->pdata.refclk_gpio2) { ret = regmap_update_bits(regmap, CS40L20_GPIO_PAD_CONTROL, @@ -1053,6 +1166,24 @@ static int cs40l20_init(struct cs40l20_private *cs40l20) return ret; } + ret = regmap_update_bits(regmap, CS40L20_DSP1_RX2_SRC, + CS40L20_DSP1_RXn_SRC_MASK, + CS40L20_DSP1_RXn_SRC_VMON + << CS40L20_DSP1_RXn_SRC_SHIFT); + if (ret) { + dev_err(dev, "Failed to route voltage monitor to DSP\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L20_DSP1_RX3_SRC, + CS40L20_DSP1_RXn_SRC_MASK, + CS40L20_DSP1_RXn_SRC_IMON + << CS40L20_DSP1_RXn_SRC_SHIFT); + if (ret) { + dev_err(dev, "Failed to route current monitor to DSP\n"); + return ret; + } + ret = regmap_update_bits(regmap, CS40L20_PWR_CTRL1, CS40L20_GLOBAL_EN_MASK, 1 << CS40L20_GLOBAL_EN_SHIFT); if (ret) { diff --git a/drivers/misc/cs40l20.h b/drivers/misc/cs40l20.h index 04c60ab18a6e..cc36ebbad60d 100644 --- a/drivers/misc/cs40l20.h +++ b/drivers/misc/cs40l20.h @@ -621,6 +621,11 @@ #define CS40L20_DAC_PCM1_SRC_SHIFT 0 #define CS40L20_DAC_PCM1_SRC_DSP1TX1 0x32 +#define CS40L20_DSP1_RXn_SRC_MASK 0x7F +#define CS40L20_DSP1_RXn_SRC_SHIFT 0 +#define CS40L20_DSP1_RXn_SRC_VMON 0x18 +#define CS40L20_DSP1_RXn_SRC_IMON 0x19 + #define CS40L20_DSP1_RESET_MASK 0x200 #define CS40L20_DSP1_RESET_SHIFT 9 @@ -698,7 +703,7 @@ #define CS40L20_XM_LIST_TERM 0xBEDEAD #define CS40L20_NUM_ALGOS_MAX 16 -#define CS40L20_FW_REV_MIN 0x000101 +#define CS40L20_FW_REV_MIN 0x000201 #define CS40L20_ALGO_ID_SIZE 4 #define CS40L20_COEFF_COUNT_SIZE 4 @@ -709,6 +714,12 @@ #define CS40L20_TIMEOUT_MS_MAX 0x02AAAA /* ~174 sec */ #define CS40L20_HALO_STATE_RUNNING 0x2 +#define CS40L20_DIAG_STATE_INIT 0x00 +#define CS40L20_DIAG_STATE_OPEN_LOOP 0x01 +#define CS40L20_DIAG_STATE_CLOSED_LOOP 0x02 +#define CS40L20_DIAG_STATE_DONE 0x03 +#define CS40L20_DIAG_STATE_DELAY_MS 1000 + #define CS40L20_MAX_WLEN 4096 #define CS40L20_DEVICE_NAME "vibrator"