misc: cs40l2x: Add Always-On Haptics disable control

* Added a sysfs control to prevent the device from automatically entering
  standby in order to reduce latency of subsequent haptic events.
* Reduced the polling interval of the playback queue for cases in which
  automatic entry into standby is disabled.

Change-Id: I5e695b33641ffe305b95f73f2eef89c94de65922
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-24 12:30:29 -05:00 • committed by a7301c
commit 4d12bb0500
2 changed files with 102 additions and 19 deletions

View file

@ -793,6 +793,62 @@ static ssize_t cs40l2x_num_waves_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", cs40l2x->num_waves);
}
static ssize_t cs40l2x_aoh_auto_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev);
bool aoh_auto_enable;
int ret;
unsigned int val;
mutex_lock(&cs40l2x->lock);
ret = regmap_read(cs40l2x->regmap, CS40L2X_PLL_CLK_CTRL, &val);
mutex_unlock(&cs40l2x->lock);
if (ret) {
pr_err("Failed to read AoH auto-enable\n");
return ret;
}
aoh_auto_enable = (val & CS40L2X_PLL_FORCE_EN_MASK)
!= CS40L2X_PLL_FORCE_EN_MASK;
return snprintf(buf, PAGE_SIZE, "%d\n", aoh_auto_enable);
}
static ssize_t cs40l2x_aoh_auto_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev);
int ret;
unsigned int val;
ret = kstrtou32(buf, 10, &val);
if (ret)
return -EINVAL;
mutex_lock(&cs40l2x->lock);
if (val)
ret = regmap_update_bits(cs40l2x->regmap,
CS40L2X_PLL_CLK_CTRL,
CS40L2X_PLL_FORCE_EN_MASK,
0 << CS40L2X_PLL_FORCE_EN_SHIFT);
else
ret = regmap_update_bits(cs40l2x->regmap,
CS40L2X_PLL_CLK_CTRL,
CS40L2X_PLL_FORCE_EN_MASK,
1 << CS40L2X_PLL_FORCE_EN_SHIFT);
mutex_unlock(&cs40l2x->lock);
if (ret) {
pr_err("Failed to write AoH auto-enable\n");
return ret;
}
return count;
}
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,
@ -817,6 +873,8 @@ static DEVICE_ATTR(dig_scale, 0660, cs40l2x_dig_scale_show,
cs40l2x_dig_scale_store);
static DEVICE_ATTR(heartbeat, 0660, cs40l2x_heartbeat_show, NULL);
static DEVICE_ATTR(num_waves, 0660, cs40l2x_num_waves_show, NULL);
static DEVICE_ATTR(aoh_auto_enable, 0660, cs40l2x_aoh_auto_enable_show,
cs40l2x_aoh_auto_enable_store);
static struct attribute *cs40l2x_dev_attrs[] = {
&dev_attr_cp_trigger_index.attr,
@ -833,6 +891,7 @@ static struct attribute *cs40l2x_dev_attrs[] = {
&dev_attr_dig_scale.attr,
&dev_attr_heartbeat.attr,
&dev_attr_num_waves.attr,
&dev_attr_aoh_auto_enable.attr,
NULL,
};
@ -865,6 +924,32 @@ static int cs40l2x_pbq_cancel(struct cs40l2x_private *cs40l2x)
return 0;
}
static int cs40l2x_pbq_poll(struct cs40l2x_private *cs40l2x)
{
bool aoh_auto_enable;
unsigned int val;
int ret;
/* this function expects to be called while mutex is locked */
if (!mutex_is_locked(&cs40l2x->lock))
return -EACCES;
ret = regmap_read(cs40l2x->regmap, CS40L2X_PLL_CLK_CTRL, &val);
if (ret)
return ret;
aoh_auto_enable = (val & CS40L2X_PLL_FORCE_EN_MASK)
!= CS40L2X_PLL_FORCE_EN_MASK;
hrtimer_start(&cs40l2x->pbq_timer,
ktime_set(0, aoh_auto_enable ?
CS40L2X_PBQ_POLL_SLOW_NS :
CS40L2X_PBQ_POLL_FAST_NS),
HRTIMER_MODE_REL);
return 0;
}
static int cs40l2x_pbq_pair_launch(struct cs40l2x_private *cs40l2x)
{
struct regmap *regmap = cs40l2x->regmap;
@ -913,9 +998,10 @@ static int cs40l2x_pbq_pair_launch(struct cs40l2x_private *cs40l2x)
if (ret)
return ret;
hrtimer_start(&cs40l2x->pbq_timer,
ktime_set(0, CS40L2X_PBQ_POLL_NS),
HRTIMER_MODE_REL);
ret = cs40l2x_pbq_poll(cs40l2x);
if (ret)
return ret;
cs40l2x->pbq_state = CS40L2X_PBQ_STATE_PLAYING;
} else {
hrtimer_start(&cs40l2x->pbq_timer,
@ -943,7 +1029,7 @@ static void cs40l2x_vibe_pbq_worker(struct work_struct *work)
switch (cs40l2x->pbq_state) {
case CS40L2X_PBQ_STATE_IDLE:
/* queue may have been canceled */
goto err_mutex;
break;
case CS40L2X_PBQ_STATE_PLAYING:
ret = regmap_read(regmap, cs40l2x_dsp_reg(cs40l2x, "STATUS",
@ -953,26 +1039,19 @@ static void cs40l2x_vibe_pbq_worker(struct work_struct *work)
goto err_mutex;
}
if (val == CS40L2X_STATUS_IDLE) {
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);
}
else
ret = cs40l2x_pbq_poll(cs40l2x);
if (ret)
dev_err(dev, "Failed to continue playback queue\n");
break;
case CS40L2X_PBQ_STATE_SILENT:
ret = cs40l2x_pbq_pair_launch(cs40l2x);
if (ret) {
if (ret)
dev_err(dev, "Failed to continue playback queue\n");
goto err_mutex;
}
break;
default:

View file

@ -547,6 +547,9 @@
#define CS40L2X_MEM_RDY_MASK 0x00000002
#define CS40L2X_MEM_RDY_SHIFT 1
#define CS40L2X_PLL_FORCE_EN_MASK 0x00010000
#define CS40L2X_PLL_FORCE_EN_SHIFT 16
#define CS40L2X_PLL_REFCLK_SEL_MASK 0x00000007
#define CS40L2X_PLL_REFCLK_SEL_SHIFT 0
#define CS40L2X_PLL_REFCLK_SEL_MCLK 0x5
@ -661,7 +664,8 @@
#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_POLL_FAST_NS 5000000
#define CS40L2X_PBQ_POLL_SLOW_NS 25000000
#define CS40L2X_PBQ_STATE_IDLE 0x00
#define CS40L2X_PBQ_STATE_PLAYING 0x01
#define CS40L2X_PBQ_STATE_SILENT 0x02