mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 13:49:24 -04:00
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:
parent
e637d16c8e
commit
c259c6853e
2 changed files with 170 additions and 28 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue