misc: cs40l2x: Add nested loops to playback queue

Added the ability to group multiple segments together and repeat them
multiple times within the queue.

Change-Id: I0eb3a4931092961e8ac332ee5f4d5507a0128c20
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-05-02 15:53:18 -05:00 • committed by a7301c
commit cc094c82cf
2 changed files with 155 additions and 73 deletions

View file

@ -142,7 +142,6 @@ static ssize_t cs40l2x_cp_trigger_queue_show(struct device *dev,
{
struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev);
ssize_t len = 0;
unsigned int tag, mag;
int i;
if (cs40l2x->pbq_depth == 0)
@ -150,25 +149,29 @@ static ssize_t cs40l2x_cp_trigger_queue_show(struct device *dev,
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;
for (i = 0; i < cs40l2x->pbq_depth; i++) {
switch (cs40l2x->pbq_pairs[i].tag) {
case CS40L2X_PBQ_TAG_SILENCE:
len += snprintf(buf + len, PAGE_SIZE - len, "%d",
cs40l2x->pbq_pairs[i].mag);
break;
case CS40L2X_PBQ_TAG_START:
len += snprintf(buf + len, PAGE_SIZE - len, "!!");
break;
case CS40L2X_PBQ_TAG_STOP:
len += snprintf(buf + len, PAGE_SIZE - len, "%d!!",
cs40l2x->pbq_pairs[i].repeat);
break;
default:
len += snprintf(buf + len, PAGE_SIZE - len, "%d.%d",
cs40l2x->pbq_pairs[i].tag,
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);
if (i < (cs40l2x->pbq_depth - 1))
len += snprintf(buf + len, PAGE_SIZE - len, ", ");
}
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");
@ -195,7 +198,8 @@ static ssize_t cs40l2x_cp_trigger_queue_store(struct device *dev,
char *pbq_seg_alloc, *pbq_seg, *pbq_seg_tok;
size_t pbq_seg_len;
unsigned int pbq_depth = 0;
unsigned int val;
unsigned int val, num_empty;
int pbq_marker = -1;
int ret;
pbq_str_alloc = kzalloc(count, GFP_KERNEL);
@ -259,22 +263,68 @@ static ssize_t cs40l2x_cp_trigger_queue_store(struct device *dev,
/* repetition specifier */
} else if (strnchr(pbq_seg, CS40L2X_PBQ_SEG_LEN_MAX, '!')) {
if (cs40l2x->pbq_repeat) {
ret = -EINVAL;
goto err_mutex;
}
val = 0;
num_empty = 0;
pbq_seg_tok = strsep(&pbq_seg, "!");
ret = kstrtou32(pbq_seg_tok, 10, &val);
if (ret) {
while (pbq_seg_tok) {
if (strnlen(pbq_seg_tok,
CS40L2X_PBQ_SEG_LEN_MAX)) {
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;
}
} else {
num_empty++;
}
pbq_seg_tok = strsep(&pbq_seg, "!");
}
/* number of empty tokens reveals specifier type */
switch (num_empty) {
case 1: /* outer loop: "n!" or "!n" */
if (cs40l2x->pbq_repeat) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_repeat = val;
break;
case 2: /* inner loop stop: "n!!" or "!!n" */
if (pbq_marker < 0) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_pairs[pbq_depth].tag =
CS40L2X_PBQ_TAG_STOP;
cs40l2x->pbq_pairs[pbq_depth].mag = pbq_marker;
cs40l2x->pbq_pairs[pbq_depth++].repeat = val;
pbq_marker = -1;
break;
case 3: /* inner loop start: "!!" */
if (pbq_marker >= 0) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_pairs[pbq_depth].tag =
CS40L2X_PBQ_TAG_START;
pbq_marker = pbq_depth++;
break;
default:
ret = -EINVAL;
goto err_mutex;
}
if (val > CS40L2X_PBQ_REPEAT_MAX) {
ret = -EINVAL;
goto err_mutex;
}
cs40l2x->pbq_repeat = val;
/* loop specifier */
} else if (strnchr(pbq_seg, CS40L2X_PBQ_SEG_LEN_MAX, '~')) {
@ -286,7 +336,8 @@ static ssize_t cs40l2x_cp_trigger_queue_store(struct device *dev,
/* duration specifier */
} else {
cs40l2x->pbq_pairs[pbq_depth].tag = 0;
cs40l2x->pbq_pairs[pbq_depth].tag =
CS40L2X_PBQ_TAG_SILENCE;
ret = kstrtou32(pbq_seg, 10, &val);
if (ret) {
@ -956,62 +1007,83 @@ 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;
int ret, i;
/* 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;
do {
/* restart queue as necessary */
if (cs40l2x->pbq_index == cs40l2x->pbq_depth) {
cs40l2x->pbq_index = 0;
for (i = 0; i < cs40l2x->pbq_depth; i++)
cs40l2x->pbq_pairs[i].remain =
cs40l2x->pbq_pairs[i].repeat;
switch (cs40l2x->pbq_remain) {
case -1:
/* loop until stopped */
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;
switch (tag) {
case CS40L2X_PBQ_TAG_SILENCE:
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++;
break;
case 0:
/* queue is finished */
cs40l2x->cp_trailer_index = 0;
cs40l2x->pbq_state = CS40L2X_PBQ_STATE_IDLE;
case CS40L2X_PBQ_TAG_START:
cs40l2x->pbq_index++;
break;
case CS40L2X_PBQ_TAG_STOP:
if (cs40l2x->pbq_pairs[cs40l2x->pbq_index].remain) {
cs40l2x->pbq_pairs[cs40l2x->pbq_index].remain--;
cs40l2x->pbq_index = mag;
} else {
cs40l2x->pbq_index++;
}
break;
default:
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);
return ret;
default:
/* loop once more */
cs40l2x->pbq_remain--;
if (ret)
return ret;
ret = regmap_write(regmap, CS40L2X_MBOX_TRIGGERINDEX,
tag);
if (ret)
return ret;
ret = cs40l2x_pbq_poll(cs40l2x);
if (ret)
return ret;
cs40l2x->pbq_state = CS40L2X_PBQ_STATE_PLAYING;
cs40l2x->pbq_index++;
}
}
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;
ret = cs40l2x_pbq_poll(cs40l2x);
if (ret)
return ret;
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++;
} while (tag == CS40L2X_PBQ_TAG_START || tag == CS40L2X_PBQ_TAG_STOP);
return 0;
}
@ -1107,7 +1179,7 @@ static void cs40l2x_vibe_start_worker(struct work_struct *work)
container_of(work, struct cs40l2x_private, vibe_start_work);
struct regmap *regmap = cs40l2x->regmap;
struct device *dev = cs40l2x->dev;
int ret;
int ret, i;
mutex_lock(&cs40l2x->lock);
@ -1159,6 +1231,11 @@ static void cs40l2x_vibe_start_worker(struct work_struct *work)
case CS40L2X_INDEX_PBQ:
cs40l2x->pbq_index = 0;
cs40l2x->pbq_remain = cs40l2x->pbq_repeat;
for (i = 0; i < cs40l2x->pbq_depth; i++)
cs40l2x->pbq_pairs[i].remain =
cs40l2x->pbq_pairs[i].repeat;
ret = cs40l2x_pbq_pair_launch(cs40l2x);
if (ret)
dev_err(dev, "Failed to launch playback queue\n");

View file

@ -700,6 +700,9 @@
#define CS40L2X_PBQ_STATE_IDLE 0x00
#define CS40L2X_PBQ_STATE_PLAYING 0x01
#define CS40L2X_PBQ_STATE_SILENT 0x02
#define CS40L2X_PBQ_TAG_SILENCE 0x0000
#define CS40L2X_PBQ_TAG_START 0x8000
#define CS40L2X_PBQ_TAG_STOP 0x8001
#define CS40L2X_DIAG_STATE_INIT 0x00
#define CS40L2X_DIAG_STATE_RUN 0x01
@ -760,6 +763,8 @@ struct cs40l2x_coeff_desc {
struct cs40l2x_pbq_pair {
unsigned int tag;
unsigned int mag;
unsigned int repeat;
unsigned int remain;
};
struct cs40l2x_dig_scale_map {