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 <jeff.labundy@cirrus.com>
Signed-off-by: Ravi Vembu <raviv@motorola.com>
This commit is contained in:
Jeff LaBundy 2018-01-23 18:15:46 -06:00 • committed by a7301c
commit c259c6853e
2 changed files with 170 additions and 28 deletions

View file

@ -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) {

View file

@ -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"