misc: cs40l2x: Add playback queue support

* Added the ability to trigger multiple waveform indexes and delays in
  a sequence based on a string passed to a sysfs control.
* Moved the call to start vibe_timer to the vibe_start_worker function
  and restricted the timer's use to continuous playback.
* Canceled vibe_timer prior to destroying vibe_workqueue.
* Replaced hard-coded waveform indexes with macros.
* Replaced all digital volume register writes with a single function.

Change-Id: I1b28bcc8d9d2a90bda432f0d3def83563baf9496
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-04-18 16:41:37 -05:00 • committed by a7301c
commit 23248efe7d
3 changed files with 557 additions and 21 deletions

View file

@ -740,3 +740,108 @@ const struct cs40l2x_otp_desc cs40l2x_otp_map[CS40L2X_NUM_OTP_MAPS] = {
.trim_table = cs40l2x_trim_table_d,
},
};
const struct cs40l2x_dig_scale_map
cs40l2x_pbq_scale[CS40L2X_PBQ_SCALE_MAX + 1] = {
{0, 0},
{1, 320},
{2, 272},
{3, 244},
{4, 224},
{5, 208},
{6, 195},
{7, 185},
{8, 176},
{9, 167},
{10, 160},
{11, 153},
{12, 147},
{13, 142},
{14, 137},
{15, 132},
{16, 127},
{17, 123},
{18, 119},
{19, 115},
{20, 112},
{21, 108},
{22, 105},
{23, 102},
{24, 99},
{25, 96},
{26, 94},
{27, 91},
{28, 88},
{29, 86},
{30, 84},
{31, 81},
{32, 79},
{33, 77},
{34, 75},
{35, 73},
{36, 71},
{37, 69},
{38, 67},
{39, 65},
{40, 64},
{41, 62},
{42, 60},
{43, 59},
{44, 57},
{45, 55},
{46, 54},
{47, 52},
{48, 51},
{49, 50},
{50, 48},
{51, 47},
{52, 45},
{53, 44},
{54, 43},
{55, 42},
{56, 40},
{57, 39},
{58, 38},
{59, 37},
{60, 35},
{61, 34},
{62, 33},
{63, 32},
{64, 31},
{65, 30},
{66, 29},
{67, 28},
{68, 27},
{69, 26},
{70, 25},
{71, 24},
{72, 23},
{73, 22},
{74, 21},
{75, 20},
{76, 19},
{77, 18},
{78, 17},
{79, 16},
{80, 16},
{81, 15},
{82, 14},
{83, 13},
{84, 12},
{85, 11},
{86, 10},
{87, 10},
{88, 9},
{89, 8},
{90, 7},
{91, 7},
{92, 6},
{93, 5},
{94, 4},
{95, 4},
{96, 3},
{97, 2},
{98, 1},
{99, 1},
{100, 0},
};

View file

@ -28,6 +28,7 @@
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/hrtimer.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/platform_data/cs40l2x.h>
@ -36,7 +37,6 @@
#ifdef CONFIG_ANDROID_TIMED_OUTPUT
#include "../staging/android/timed_output.h"
#include <linux/hrtimer.h>
#else
#include <linux/leds.h>
#endif /* CONFIG_ANDROID_TIMED_OUTPUT */
@ -49,6 +49,7 @@ struct cs40l2x_private {
unsigned int devid;
unsigned int revid;
struct work_struct vibe_start_work;
struct work_struct vibe_pbq_work;
struct work_struct vibe_stop_work;
struct workqueue_struct *vibe_workqueue;
struct mutex lock;
@ -67,9 +68,17 @@ struct cs40l2x_private {
unsigned int f0_measured;
unsigned int redc_measured;
unsigned int dig_scale;
struct cs40l2x_pbq_pair pbq_pairs[CS40L2X_PBQ_DEPTH_MAX];
struct hrtimer pbq_timer;
unsigned int pbq_depth;
unsigned int pbq_index;
unsigned int pbq_state;
int pbq_repeat;
int pbq_remain;
#ifdef CONFIG_ANDROID_TIMED_OUTPUT
struct timed_output_dev timed_dev;
struct hrtimer vibe_timer;
int vibe_timeout;
#else
struct led_classdev led_dev;
#endif /* CONFIG_ANDROID_TIMED_OUTPUT */
@ -119,6 +128,7 @@ static ssize_t cs40l2x_cp_trigger_index_store(struct device *dev,
return -EINVAL;
if ((index & CS40L2X_INDEX_MASK) >= cs40l2x->num_waves
&& index != CS40L2X_INDEX_PBQ
&& index != CS40L2X_INDEX_DIAG)
return -EINVAL;
@ -127,6 +137,188 @@ static ssize_t cs40l2x_cp_trigger_index_store(struct device *dev,
return count;
}
static ssize_t cs40l2x_cp_trigger_queue_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev);
ssize_t len = 0;
unsigned int tag, mag;
int i;
if (cs40l2x->pbq_depth == 0)
return -ENODATA;
mutex_lock(&cs40l2x->lock);
for (i = 0; i < cs40l2x->pbq_depth - 1; i++) {
tag = cs40l2x->pbq_pairs[i].tag;
mag = cs40l2x->pbq_pairs[i].mag;
if (tag)
len += snprintf(buf + len, PAGE_SIZE - len, "%d.%d, ",
tag, mag);
else
len += snprintf(buf + len, PAGE_SIZE - len, "%d, ",
mag);
}
tag = cs40l2x->pbq_pairs[i].tag;
mag = cs40l2x->pbq_pairs[i].mag;
if (tag)
len += snprintf(buf + len, PAGE_SIZE - len, "%d.%d", tag, mag);
else
len += snprintf(buf + len, PAGE_SIZE - len, "%d", mag);
switch (cs40l2x->pbq_repeat) {
case -1:
len += snprintf(buf + len, PAGE_SIZE - len, ", ~\n");
break;
case 0:
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
break;
default:
len += snprintf(buf + len, PAGE_SIZE - len, ", %d!\n",
cs40l2x->pbq_repeat);
}
mutex_unlock(&cs40l2x->lock);
return len;
}
static ssize_t cs40l2x_cp_trigger_queue_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev);
char *pbq_str_alloc, *pbq_str, *pbq_str_tok;
char *pbq_seg_alloc, *pbq_seg, *pbq_seg_tok;
unsigned int pbq_depth = 0;
unsigned int val;
int ret;
pbq_str_alloc = kzalloc(count, GFP_KERNEL);
if (!pbq_str_alloc)
return -ENOMEM;
pbq_seg_alloc = kzalloc(CS40L2X_PBQ_SEG_LEN_MAX + 1, GFP_KERNEL);
if (!pbq_seg_alloc) {
kfree(pbq_str_alloc);
return -ENOMEM;
}
mutex_lock(&cs40l2x->lock);
cs40l2x->pbq_depth = 0;
cs40l2x->pbq_repeat = 0;
pbq_str = pbq_str_alloc;
strcpy(pbq_str, buf);
pbq_str_tok = strsep(&pbq_str, ",");
while (pbq_str_tok) {
if (strlen(strim(pbq_str_tok)) > CS40L2X_PBQ_SEG_LEN_MAX) {
ret = -E2BIG;
goto err_mutex;
}
pbq_seg = pbq_seg_alloc;
strcpy(pbq_seg, strim(pbq_str_tok));
/* waveform specifier */
if (strchr(pbq_seg, '.')) {
/* index */
pbq_seg_tok = strsep(&pbq_seg, ".");
ret = kstrtou32(pbq_seg_tok, 10, &val);
if (ret) {
ret = -EINVAL;
goto err_mutex;
}
if (val == 0 || val >= cs40l2x->num_waves) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_pairs[pbq_depth].tag = val;
/* scale */
pbq_seg_tok = strsep(&pbq_seg, ".");
ret = kstrtou32(pbq_seg_tok, 10, &val);
if (ret) {
ret = -EINVAL;
goto err_mutex;
}
if (val == 0 || val > CS40L2X_PBQ_SCALE_MAX) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_pairs[pbq_depth++].mag = val;
/* repetition specifier */
} else if (strchr(pbq_seg, '!')) {
if (cs40l2x->pbq_repeat) {
ret = -EINVAL;
goto err_mutex;
}
pbq_seg_tok = strsep(&pbq_seg, "!");
ret = kstrtou32(pbq_seg_tok, 10, &val);
if (ret) {
ret = -EINVAL;
goto err_mutex;
}
if (val > CS40L2X_PBQ_REPEAT_MAX) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_repeat = val;
/* loop specifier */
} else if (strchr(pbq_seg, '~')) {
if (cs40l2x->pbq_repeat) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_repeat = -1;
/* duration specifier */
} else {
cs40l2x->pbq_pairs[pbq_depth].tag = 0;
ret = kstrtou32(pbq_seg, 10, &val);
if (ret) {
ret = -EINVAL;
goto err_mutex;
}
if (val > CS40L2X_PBQ_DELAY_MAX) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_pairs[pbq_depth++].mag = val;
}
if (pbq_depth == CS40L2X_PBQ_DEPTH_MAX) {
ret = -E2BIG;
goto err_mutex;
}
pbq_str_tok = strsep(&pbq_str, ",");
}
cs40l2x->pbq_depth = pbq_depth;
ret = count;
err_mutex:
mutex_unlock(&cs40l2x->lock);
kfree(pbq_str_alloc);
kfree(pbq_seg_alloc);
return ret;
}
static unsigned int cs40l2x_dsp_reg(struct cs40l2x_private *cs40l2x,
const char *coeff_name, const unsigned char block_type)
{
@ -520,6 +712,21 @@ static ssize_t cs40l2x_comp_enable_store(struct device *dev,
return count;
}
static int cs40l2x_dig_scale_set(struct cs40l2x_private *cs40l2x,
unsigned int dig_scale)
{
/* this function expects to be called while mutex is locked */
if (!mutex_is_locked(&cs40l2x->lock))
return -EACCES;
return regmap_update_bits(cs40l2x->regmap,
CS40L2X_AMP_DIG_VOL_CTRL,
CS40L2X_AMP_VOL_PCM_MASK,
((CS40L2X_DIG_SCALE_ZERO - dig_scale)
& CS40L2X_DIG_SCALE_MASK)
<< CS40L2X_AMP_VOL_PCM_SHIFT);
}
static ssize_t cs40l2x_dig_scale_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -544,10 +751,7 @@ static ssize_t cs40l2x_dig_scale_store(struct device *dev,
return -EINVAL;
mutex_lock(&cs40l2x->lock);
ret = regmap_update_bits(cs40l2x->regmap, CS40L2X_AMP_DIG_VOL_CTRL,
CS40L2X_AMP_VOL_PCM_MASK,
((0x800 - dig_scale) & 0x7FF)
<< CS40L2X_AMP_VOL_PCM_SHIFT);
cs40l2x_dig_scale_set(cs40l2x, dig_scale);
mutex_unlock(&cs40l2x->lock);
if (ret) {
@ -591,6 +795,8 @@ static ssize_t cs40l2x_num_waves_show(struct device *dev,
static DEVICE_ATTR(cp_trigger_index, 0660, cs40l2x_cp_trigger_index_show,
cs40l2x_cp_trigger_index_store);
static DEVICE_ATTR(cp_trigger_queue, 0660, cs40l2x_cp_trigger_queue_show,
cs40l2x_cp_trigger_queue_store);
static DEVICE_ATTR(gpio1_rise_index, 0660, cs40l2x_gpio1_rise_index_show,
cs40l2x_gpio1_rise_index_store);
static DEVICE_ATTR(gpio1_fall_index, 0660, cs40l2x_gpio1_fall_index_show,
@ -614,6 +820,7 @@ static DEVICE_ATTR(num_waves, 0660, cs40l2x_num_waves_show, NULL);
static struct attribute *cs40l2x_dev_attrs[] = {
&dev_attr_cp_trigger_index.attr,
&dev_attr_cp_trigger_queue.attr,
&dev_attr_gpio1_rise_index.attr,
&dev_attr_gpio1_fall_index.attr,
&dev_attr_gpio1_fall_timeout.attr,
@ -633,6 +840,160 @@ static struct attribute_group cs40l2x_dev_attr_group = {
.attrs = cs40l2x_dev_attrs,
};
static int cs40l2x_pbq_cancel(struct cs40l2x_private *cs40l2x)
{
struct regmap *regmap = cs40l2x->regmap;
int ret;
/* this function expects to be called from a locked worker function */
if (!mutex_is_locked(&cs40l2x->lock))
return -EACCES;
hrtimer_cancel(&cs40l2x->pbq_timer);
ret = regmap_write(regmap, cs40l2x_dsp_reg(cs40l2x, "ENDPLAYBACK",
CS40L2X_XM_UNPACKED_TYPE), 1);
if (ret)
return ret;
ret = cs40l2x_dig_scale_set(cs40l2x, cs40l2x->dig_scale);
if (ret)
return ret;
cs40l2x->pbq_state = CS40L2X_PBQ_STATE_IDLE;
return 0;
}
static int cs40l2x_pbq_pair_launch(struct cs40l2x_private *cs40l2x)
{
struct regmap *regmap = cs40l2x->regmap;
unsigned int dig_scale = cs40l2x->dig_scale;
unsigned int tag, mag;
int ret;
/* this function expects to be called from a locked worker function */
if (!mutex_is_locked(&cs40l2x->lock))
return -EACCES;
if (cs40l2x->pbq_index == cs40l2x->pbq_depth) {
cs40l2x->pbq_index = 0;
switch (cs40l2x->pbq_remain) {
case -1:
/* loop until stopped */
break;
case 0:
/* queue is finished */
cs40l2x->cp_trailer_index = 0;
cs40l2x->pbq_state = CS40L2X_PBQ_STATE_IDLE;
ret = cs40l2x_dig_scale_set(cs40l2x, dig_scale);
return ret;
default:
/* loop once more */
cs40l2x->pbq_remain--;
}
}
tag = cs40l2x->pbq_pairs[cs40l2x->pbq_index].tag;
mag = cs40l2x->pbq_pairs[cs40l2x->pbq_index].mag;
/* zero tag indicates a period of silence */
if (tag) {
dig_scale += cs40l2x_pbq_scale[mag].dig_scale;
if (dig_scale > CS40L2X_DIG_SCALE_MAX)
dig_scale = CS40L2X_DIG_SCALE_MAX;
ret = cs40l2x_dig_scale_set(cs40l2x, dig_scale);
if (ret)
return ret;
ret = regmap_write(regmap, CS40L2X_MBOX_TRIGGERINDEX, tag);
if (ret)
return ret;
hrtimer_start(&cs40l2x->pbq_timer,
ktime_set(0, CS40L2X_PBQ_POLL_NS),
HRTIMER_MODE_REL);
cs40l2x->pbq_state = CS40L2X_PBQ_STATE_PLAYING;
} else {
hrtimer_start(&cs40l2x->pbq_timer,
ktime_set(mag / 1000, (mag % 1000) * 1000000),
HRTIMER_MODE_REL);
cs40l2x->pbq_state = CS40L2X_PBQ_STATE_SILENT;
}
cs40l2x->pbq_index++;
return 0;
}
static void cs40l2x_vibe_pbq_worker(struct work_struct *work)
{
struct cs40l2x_private *cs40l2x =
container_of(work, struct cs40l2x_private, vibe_pbq_work);
struct regmap *regmap = cs40l2x->regmap;
struct device *dev = cs40l2x->dev;
unsigned int val;
int ret;
mutex_lock(&cs40l2x->lock);
switch (cs40l2x->pbq_state) {
case CS40L2X_PBQ_STATE_IDLE:
/* queue may have been canceled */
goto err_mutex;
case CS40L2X_PBQ_STATE_PLAYING:
ret = regmap_read(regmap, cs40l2x_dsp_reg(cs40l2x, "STATUS",
CS40L2X_XM_UNPACKED_TYPE), &val);
if (ret) {
dev_err(dev, "Failed to capture playback status\n");
goto err_mutex;
}
if (val == CS40L2X_STATUS_IDLE) {
ret = cs40l2x_pbq_pair_launch(cs40l2x);
if (ret) {
dev_err(dev,
"Failed to continue playback queue\n");
goto err_mutex;
}
} else {
hrtimer_start(&cs40l2x->pbq_timer,
ktime_set(0, CS40L2X_PBQ_POLL_NS),
HRTIMER_MODE_REL);
}
break;
case CS40L2X_PBQ_STATE_SILENT:
ret = cs40l2x_pbq_pair_launch(cs40l2x);
if (ret) {
dev_err(dev, "Failed to continue playback queue\n");
goto err_mutex;
}
break;
default:
dev_err(dev, "Unexpected playback queue state: %d\n",
cs40l2x->pbq_state);
}
err_mutex:
mutex_unlock(&cs40l2x->lock);
}
static enum hrtimer_restart cs40l2x_pbq_timer(struct hrtimer *timer)
{
struct cs40l2x_private *cs40l2x =
container_of(timer, struct cs40l2x_private, pbq_timer);
queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_pbq_work);
return HRTIMER_NORESTART;
}
static int cs40l2x_diag_capture(struct cs40l2x_private *cs40l2x)
{
struct regmap *regmap = cs40l2x->regmap;
@ -670,36 +1031,63 @@ static void cs40l2x_vibe_start_worker(struct work_struct *work)
mutex_lock(&cs40l2x->lock);
/* gracefully exit if diagnostics stimulus is interrupted */
if (cs40l2x->cp_trailer_index == CS40L2X_INDEX_DIAG) {
/* handle interruption of special cases */
switch (cs40l2x->cp_trailer_index) {
case CS40L2X_INDEX_PBQ:
ret = cs40l2x_pbq_cancel(cs40l2x);
if (ret)
dev_err(dev, "Failed to cancel playback queue\n");
break;
case CS40L2X_INDEX_DIAG:
cs40l2x->diag_state = CS40L2X_DIAG_STATE_INIT;
goto err_mutex;
}
cs40l2x->cp_trailer_index = cs40l2x->cp_trigger_index;
#ifdef CONFIG_ANDROID_TIMED_OUTPUT
/* launch timer in the event of continuous playback */
switch (cs40l2x->cp_trailer_index) {
case 0x0000:
case 0x8000 ... 0xFFFE:
case CS40L2X_INDEX_VIBE:
case CS40L2X_INDEX_CONT_MIN ... CS40L2X_INDEX_CONT_MAX:
case CS40L2X_INDEX_DIAG:
hrtimer_start(&cs40l2x->vibe_timer,
ktime_set(cs40l2x->vibe_timeout / 1000,
(cs40l2x->vibe_timeout % 1000)
* 1000000),
HRTIMER_MODE_REL);
}
#endif /* CONFIG_ANDROID_TIMED_OUTPUT */
switch (cs40l2x->cp_trailer_index) {
case CS40L2X_INDEX_VIBE:
case CS40L2X_INDEX_CONT_MIN ... CS40L2X_INDEX_CONT_MAX:
ret = regmap_write(regmap, CS40L2X_MBOX_TRIGGER_MS,
cs40l2x->cp_trailer_index & CS40L2X_INDEX_MASK);
if (ret)
dev_err(dev, "Failed to start playback\n");
break;
case 0x0001 ... 0x7FFF:
case CS40L2X_INDEX_CLICK_MIN ... CS40L2X_INDEX_CLICK_MAX:
ret = regmap_write(regmap, CS40L2X_MBOX_TRIGGERINDEX,
cs40l2x->cp_trailer_index);
if (ret)
dev_err(dev, "Failed to start playback\n");
break;
case CS40L2X_INDEX_PBQ:
cs40l2x->pbq_index = 0;
cs40l2x->pbq_remain = cs40l2x->pbq_repeat;
ret = cs40l2x_pbq_pair_launch(cs40l2x);
if (ret)
dev_err(dev, "Failed to launch playback queue\n");
break;
case CS40L2X_INDEX_DIAG:
cs40l2x->diag_state = CS40L2X_DIAG_STATE_INIT;
ret = regmap_update_bits(cs40l2x->regmap,
CS40L2X_AMP_DIG_VOL_CTRL,
CS40L2X_AMP_VOL_PCM_MASK, 0);
ret = cs40l2x_dig_scale_set(cs40l2x, 0);
if (ret) {
dev_err(dev, "Failed to reset digital scale\n");
goto err_mutex;
@ -760,15 +1148,17 @@ static void cs40l2x_vibe_stop_worker(struct work_struct *work)
if (ret)
dev_err(dev, "Failed to disable stimulus mode\n");
ret = regmap_update_bits(cs40l2x->regmap,
CS40L2X_AMP_DIG_VOL_CTRL,
CS40L2X_AMP_VOL_PCM_MASK,
((0x800 - cs40l2x->dig_scale) & 0x7FF)
<< CS40L2X_AMP_VOL_PCM_SHIFT);
ret = cs40l2x_dig_scale_set(cs40l2x, cs40l2x->dig_scale);
if (ret)
dev_err(dev, "Failed to restore digital scale\n");
break;
case CS40L2X_INDEX_PBQ:
ret = cs40l2x_pbq_cancel(cs40l2x);
if (ret)
dev_err(dev, "Failed to cancel playback queue\n");
break;
default:
ret = regmap_write(regmap,
cs40l2x_dsp_reg(cs40l2x, "ENDPLAYBACK",
@ -790,9 +1180,7 @@ static void cs40l2x_vibe_enable(struct timed_output_dev *sdev, int timeout)
container_of(sdev, struct cs40l2x_private, timed_dev);
if (timeout > 0) {
hrtimer_start(&cs40l2x->vibe_timer,
ktime_set(timeout / 1000, (timeout % 1000) * 1000000),
HRTIMER_MODE_REL);
cs40l2x->vibe_timeout = timeout;
queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_start_work);
} else {
hrtimer_cancel(&cs40l2x->vibe_timer);
@ -894,6 +1282,8 @@ static void cs40l2x_create_led(struct cs40l2x_private *cs40l2x)
static void cs40l2x_vibe_init(struct cs40l2x_private *cs40l2x)
{
struct hrtimer *pbq_timer = &cs40l2x->pbq_timer;
#ifdef CONFIG_ANDROID_TIMED_OUTPUT
cs40l2x_create_timed_output(cs40l2x);
#else
@ -908,8 +1298,12 @@ static void cs40l2x_vibe_init(struct cs40l2x_private *cs40l2x)
}
INIT_WORK(&cs40l2x->vibe_start_work, cs40l2x_vibe_start_worker);
INIT_WORK(&cs40l2x->vibe_pbq_work, cs40l2x_vibe_pbq_worker);
INIT_WORK(&cs40l2x->vibe_stop_work, cs40l2x_vibe_stop_worker);
hrtimer_init(pbq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pbq_timer->function = cs40l2x_pbq_timer;
cs40l2x->vibe_init_success = true;
}
@ -2034,6 +2428,8 @@ static int cs40l2x_i2c_remove(struct i2c_client *i2c_client)
if (cs40l2x->vibe_init_success) {
#ifdef CONFIG_ANDROID_TIMED_OUTPUT
hrtimer_cancel(&cs40l2x->vibe_timer);
timed_output_dev_unregister(&cs40l2x->timed_dev);
sysfs_remove_group(&cs40l2x->timed_dev.dev->kobj,
@ -2045,7 +2441,10 @@ static int cs40l2x_i2c_remove(struct i2c_client *i2c_client)
&cs40l2x_dev_attr_group);
#endif /* CONFIG_ANDROID_TIMED_OUTPUT */
hrtimer_cancel(&cs40l2x->pbq_timer);
cancel_work_sync(&cs40l2x->vibe_start_work);
cancel_work_sync(&cs40l2x->vibe_pbq_work);
cancel_work_sync(&cs40l2x->vibe_stop_work);
destroy_workqueue(cs40l2x->vibe_workqueue);

View file

@ -641,14 +641,31 @@
#define CS40L2X_TIMEOUT_MS_MAX 0x02AAAA /* ~174 sec */
#define CS40L2X_HALO_STATE_RUNNING 0x2
#define CS40L2X_STATUS_IDLE 0xFFFFFF
#define CS40L2X_MBOX_TRIGGERINDEX CS40L2X_DSP_VIRT1_MBOX_1
#define CS40L2X_MBOX_TRIGGER_MS CS40L2X_DSP_VIRT1_MBOX_2
#define CS40L2X_MBOX_STIMULUS_MODE CS40L2X_DSP_VIRT1_MBOX_3
#define CS40L2X_INDEX_MASK 0x7FFF
#define CS40L2X_INDEX_VIBE 0x0000
#define CS40L2X_INDEX_CLICK_MIN 0x0001
#define CS40L2X_INDEX_CLICK_MAX 0x7FFF
#define CS40L2X_INDEX_CONT_MIN 0x8000
#define CS40L2X_INDEX_CONT_MAX 0xFFFD
#define CS40L2X_INDEX_PBQ 0xFFFE
#define CS40L2X_INDEX_DIAG 0xFFFF
#define CS40L2X_PBQ_SEG_LEN_MAX 10
#define CS40L2X_PBQ_DEPTH_MAX 256
#define CS40L2X_PBQ_SCALE_MAX 100
#define CS40L2X_PBQ_DELAY_MAX 10000
#define CS40L2X_PBQ_REPEAT_MAX 16
#define CS40L2X_PBQ_POLL_NS 25000000
#define CS40L2X_PBQ_STATE_IDLE 0x00
#define CS40L2X_PBQ_STATE_PLAYING 0x01
#define CS40L2X_PBQ_STATE_SILENT 0x02
#define CS40L2X_DIAG_STATE_INIT 0x00
#define CS40L2X_DIAG_STATE_RUN 0x01
#define CS40L2X_DIAG_STATE_DONE 0x02
@ -665,6 +682,8 @@
#define CS40L2X_TEST_KEY_RELOCK_CODE2 0x33
#define CS40L2X_DIG_SCALE_MAX 816 /* -102 dB */
#define CS40L2X_DIG_SCALE_ZERO 0x800
#define CS40L2X_DIG_SCALE_MASK 0x7FF
#define CS40L2X_MAX_WLEN 4096
@ -701,10 +720,23 @@ struct cs40l2x_coeff_desc {
struct list_head list;
};
struct cs40l2x_pbq_pair {
unsigned int tag;
unsigned int mag;
};
struct cs40l2x_dig_scale_map {
unsigned int encoded;
unsigned int dig_scale;
};
extern const unsigned char cs40l2x_bst_k1_table[4][5];
extern const unsigned char cs40l2x_bst_k2_table[4][5];
extern const unsigned char cs40l2x_bst_slope_table[4];
extern const struct cs40l2x_otp_desc cs40l2x_otp_map[CS40L2X_NUM_OTP_MAPS];
extern const struct cs40l2x_dig_scale_map
cs40l2x_pbq_scale[CS40L2X_PBQ_SCALE_MAX + 1];
#endif /*__CS40L2X_H__*/